Bölüm adı olmadan bir dosyayı okumak için ConfigParser'ı kullanma


90

Ben kullanıyorum ConfigParserBir komut dosyasının çalışma zamanı yapılandırmasını okumak için .

Bir bölüm adı vermeme esnekliğine sahip olmak istiyorum (yeterince basit olan komut dosyaları var; bir 'bölüm'e ihtiyaçları yok). ConfigParserbir NoSectionErroristisna atacak ve dosyayı kabul etmeyecektir.

ConfigParser'ın (key, value) bölüm adları olmayan bir yapılandırma dosyasının ?

Örneğin:

key1=val1
key2:val2

Yapılandırma dosyasına yazmamayı tercih ederim.


Yanıtlar:


53

Alex Martelli, (görünüşe göre bölümsüz yapılandırma dosyaları olan) dosyaları ayrıştırmak için bir çözüm sağladı .ConfigParser.properties

Çözümü , ConfigParsergereksinimlerini karşılamak için otomatik olarak bir kukla bölüm başlığı ekleyen dosya benzeri bir sarmalayıcıdır .


+1 çünkü tam olarak önermek üzereydim. Tek yapmanız gereken sadece bir bölüm eklemek varken neden tüm karmaşıklığı ekleyin!
jathanism

5
@jathanism: Mevcut yapılandırma / özellikler dosyalarıyla çalışmak istediğiniz, var olan Java kodu tarafından okunan ve bu başlıkları değiştirme riskini bilmediğiniz durumlar vardır
tshepang

44

Jterrace'in verdiği bu cevapla aydınlanarak şu çözümü buldum:

  1. Tüm dosyayı bir dizeye oku
  2. Varsayılan bölüm adına sahip önek
  3. Dosya benzeri bir nesneyi taklit etmek için StringIO kullanın
ini_str = '[root]\n' + open(ini_path, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
config = ConfigParser.RawConfigParser()
config.readfp(ini_fp)


Gelecekteki Google çalışanları için DÜZENLE : Python 3.4+ readfpitibariyle kullanımdan kaldırılmıştır ve StringIOartık gerekli değildir. Bunun yerine read_stringdoğrudan kullanabiliriz :

with open('config_file') as f:
    file_content = '[dummy_section]\n' + f.read()

config_parser = ConfigParser.RawConfigParser()
config_parser.read_string(file_content)

Bu aynı zamanda basit bir Makefile'ı (yalnızca takma adlarla) ayrıştırmak için harikalar yaratır! Bu cevaptan esinlenerek , Python'daki tam komutlarıyla takma adları değiştirmek için tam bir komut dosyası .
gaborous

42

Bunu tek bir kod satırında yapabilirsiniz.

Python 3'te, yapılandırma dosyası verilerinizin başına sahte bir bölüm başlığı ekleyin ve bunu iletin read_string().

from configparser import ConfigParser

parser = ConfigParser()
with open("foo.conf") as stream:
    parser.read_string("[top]\n" + stream.read())  # This line does the trick.

itertools.chain()Bir bölüm başlığını simüle etmek için de kullanabilirsiniz read_file(). Bu, yukarıdaki yaklaşımdan daha fazla bellek açısından verimli olabilir ve kısıtlı bir çalışma zamanı ortamında büyük yapılandırma dosyalarınız varsa yararlı olabilir.

from configparser import ConfigParser
from itertools import chain

parser = ConfigParser()
with open("foo.conf") as lines:
    lines = chain(("[top]",), lines)  # This line does the trick.
    parser.read_file(lines)

Python 2'de, yapılandırma dosyası verilerinizin başına sahte bir bölüm başlığı ekleyin, sonucu bir StringIOnesneye sarın ve ona iletin readfp().

from ConfigParser import ConfigParser
from StringIO import StringIO

parser = ConfigParser()
with open("foo.conf") as stream:
    stream = StringIO("[top]\n" + stream.read())  # This line does the trick.
    parser.readfp(stream)

Bu yaklaşımlardan herhangi biriyle, yapılandırma ayarlarınız şurada mevcut olacaktır: parser.items('top') .

StringIO'yu python 3'te de kullanabilirsiniz, belki hem eski hem de yeni python yorumlayıcıları ile uyumluluk için, ancak şimdi iopakette bulunduğunu ve readfp()artık kullanımdan kaldırıldığını unutmayın.

Alternatif olarak, ConfigParser yerine bir TOML ayrıştırıcısı kullanmayı düşünebilirsiniz .


18

Bunu basitçe yapmak için ConfigObj kitaplığını kullanabilirsiniz: http://www.voidspace.org.uk/python/configobj.html

Güncellendi: En son kodu burada bulun .

Debian / Ubuntu altındaysanız, paket yöneticinizi kullanarak bu modülü kurabilirsiniz:

apt-get install python-configobj

Bir kullanım örneği:

from configobj import ConfigObj

config = ConfigObj('myConfigFile.ini')
config.get('key1') # You will get val1
config.get('key2') # You will get val2

8

Bunu yapmanın en kolay yolu bence python'un CSV ayrıştırıcısını kullanmaktır. İşte bu yaklaşımı ve bir test sürücüsünü gösteren bir okuma / yazma işlevi. Değerlerin çok satırlı olmasına izin verilmediği sürece bu çalışmalıdır. :)

import csv
import operator

def read_properties(filename):
    """ Reads a given properties file with each line of the format key=value.  Returns a dictionary containing the pairs.

    Keyword arguments:
        filename -- the name of the file to be read
    """
    result={ }
    with open(filename, "rb") as csvfile:
        reader = csv.reader(csvfile, delimiter='=', escapechar='\\', quoting=csv.QUOTE_NONE)
        for row in reader:
            if len(row) != 2:
                raise csv.Error("Too many fields on row with contents: "+str(row))
            result[row[0]] = row[1] 
    return result

def write_properties(filename,dictionary):
    """ Writes the provided dictionary in key-sorted order to a properties file with each line of the format key=value

    Keyword arguments:
        filename -- the name of the file to be written
        dictionary -- a dictionary containing the key/value pairs.
    """
    with open(filename, "wb") as csvfile:
        writer = csv.writer(csvfile, delimiter='=', escapechar='\\', quoting=csv.QUOTE_NONE)
        for key, value in sorted(dictionary.items(), key=operator.itemgetter(0)):
                writer.writerow([ key, value])

def main():
    data={
        "Hello": "5+5=10",
        "World": "Snausage",
        "Awesome": "Possum"
    }

    filename="test.properties"
    write_properties(filename,data)
    newdata=read_properties(filename)

    print "Read in: "
    print newdata
    print

    contents=""
    with open(filename, 'rb') as propfile:
        contents=propfile.read()
    print "File contents:"
    print contents

    print ["Failure!", "Success!"][data == newdata]
    return

if __name__ == '__main__': 
     main() 

+1 csvGenel ConfigParserşikayeti çözmek için modülün akıllıca kullanılması . Kolayca genelleştirildi ve hem Python 2 hem de 3 uyumlu olacak şekilde yapıldı .
martineau

6

Bu problemle kendim karşılaştıktan sonra, Alex Martelli'nin kabul edilen yanıtla bağlantılı yaklaşımına dayanarak, dosyaları bölümler olmadan şeffaf bir şekilde okuyabilen ve yazabilen ConfigParser'a (Python 2'deki sürüm) eksiksiz bir paketleyici yazdım. Herhangi bir ConfigParser kullanımının yerine geçmelidir. İhtiyaç duyan birinin bu sayfayı bulması ihtimaline karşı yayınlamak.

import ConfigParser
import StringIO

class SectionlessConfigParser(ConfigParser.RawConfigParser):
    """
    Extends ConfigParser to allow files without sections.

    This is done by wrapping read files and prepending them with a placeholder
    section, which defaults to '__config__'
    """

    def __init__(self, *args, **kwargs):
        default_section = kwargs.pop('default_section', None)
        ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)

        self._default_section = None
        self.set_default_section(default_section or '__config__')

    def get_default_section(self):
        return self._default_section

    def set_default_section(self, section):
        self.add_section(section)

        # move all values from the previous default section to the new one
        try:
            default_section_items = self.items(self._default_section)
            self.remove_section(self._default_section)
        except ConfigParser.NoSectionError:
            pass
        else:
            for (key, value) in default_section_items:
                self.set(section, key, value)

        self._default_section = section

    def read(self, filenames):
        if isinstance(filenames, basestring):
            filenames = [filenames]

        read_ok = []
        for filename in filenames:
            try:
                with open(filename) as fp:
                    self.readfp(fp)
            except IOError:
                continue
            else:
                read_ok.append(filename)

        return read_ok

    def readfp(self, fp, *args, **kwargs):
        stream = StringIO()

        try:
            stream.name = fp.name
        except AttributeError:
            pass

        stream.write('[' + self._default_section + ']\n')
        stream.write(fp.read())
        stream.seek(0, 0)

        return ConfigParser.RawConfigParser.readfp(self, stream, *args,
                                                   **kwargs)

    def write(self, fp):
        # Write the items from the default section manually and then remove them
        # from the data. They'll be re-added later.
        try:
            default_section_items = self.items(self._default_section)
            self.remove_section(self._default_section)

            for (key, value) in default_section_items:
                fp.write("{0} = {1}\n".format(key, value))

            fp.write("\n")
        except ConfigParser.NoSectionError:
            pass

        ConfigParser.RawConfigParser.write(self, fp)

        self.add_section(self._default_section)
        for (key, value) in default_section_items:
            self.set(self._default_section, key, value)

5

Blueicefield'ın cevabı configobj'den bahsediyordu, ancak orijinal kitaplık yalnızca Python 2'yi destekliyor. Artık Python 3+ uyumlu bir bağlantı noktasına sahip:

https://github.com/DiffSK/configobj

API'ler değişmedi, belgesine bakın .


Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse yalnızca bağlantı yanıtları geçersiz hale gelebilir. - Yorumdan
ks

Ya API değişirse? Cevabı, bir bağlantı veya kod parçacığı gibi güncel değilse her zaman düzenleyebilirsiniz. Bunu defalarca yaptım, neden bunun bir sorun olduğunu düşündüğünü anlamıyorum.
laike9m
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.