ConfigParser를 사용하여 섹션 이름없이 파일 읽기


87

ConfigParser스크립트의 런타임 구성을 읽는 데 사용 하고 있습니다.

섹션 이름을 제공하지 않는 유연성을 갖고 싶습니다 (충분히 간단한 스크립트가 있으며 '섹션'이 필요하지 않음). 예외 ConfigParserNoSectionError발생하고 파일을 수락하지 않습니다.

ConfigParser가 (key, value)섹션 이름없이 구성 파일 의 튜플을 검색하도록하려면 어떻게해야합니까?

예를 들면 :

key1=val1
key2:val2

차라리 구성 파일에 쓰지 않습니다.


답변:


52

Alex Martelli 파일 (분명히 섹션없는 구성 파일) ConfigParser을 구문 분석 하는 데 사용 하는 솔루션제공했습니다.properties .

그의 솔루션ConfigParser의 요구 사항 을 충족시키기 위해 더미 섹션 제목을 자동으로 삽입하는 파일과 같은 래퍼입니다 .


+1은 제가 제안하려는 내용이기 때문입니다. 섹션을 추가하기 만하면되는데 모든 복잡성을 추가하는 이유는 무엇입니까?
jathanism

5
@jathanism : 기존 Java 코드로 읽은 기존 구성 / 속성 파일로 작업하려는 경우가 있으며 해당 헤더를 수정할 위험을 알지 못합니다
tshepang

43

jterrace 의이 답변에 의해 깨달음 이이 솔루션을 생각해 냈습니다.

  1. 전체 파일을 문자열로 읽기
  2. 기본 섹션 이름이있는 접두사
  3. StringIO를 사용하여 파일 류 객체 모방
ini_str = '[root]\n' + open(ini_path, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
config = ConfigParser.RawConfigParser()
config.readfp(ini_fp)


향후 Google 사용자를위한 수정 : Python 3.4 readfp이상에서는 StringIO더 이상 사용되지 않으며 더 이상 필요하지 않습니다. 대신 다음을 read_string직접 사용할 수 있습니다.

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

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

이것은 또한 (별명 만있는) 간단한 Makefile을 파싱하는데 놀라운 일입니다! 다음은 이 답변에서 영감을 얻은 Python의 전체 명령으로 별칭을 대체하는 전체 스크립트 입니다.
gaborous

42

한 줄의 코드로이 작업을 수행 할 수 있습니다.

Python 3에서는 구성 파일 데이터에 가짜 섹션 헤더를 추가하고 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()대한 섹션 헤더를 시뮬레이션하는 데 사용할 수도 있습니다 read_file(). 이는 위의 접근 방식보다 메모리 효율성이 더 높을 수 있으며, 제한된 런타임 환경에 큰 구성 파일이있는 경우 유용 할 수 있습니다.

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에서는 구성 파일 데이터에 가짜 섹션 헤더를 추가하고 결과를 StringIO객체로 래핑 한 다음 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)

이러한 접근 방식 중 하나를 사용하면 구성 설정을 parser.items('top').

이전 및 새 파이썬 인터프리터와의 호환성을 위해 Python 3에서도 StringIO를 사용할 수 있지만 이제는 io패키지에 있으며 readfp()더 이상 사용되지 않습니다.

또는 ConfigParser 대신 TOML 파서를 사용하는 것을 고려할 수 있습니다 .


18

ConfigObj 라이브러리를 사용하여 간단하게 수행 할 수 있습니다. http://www.voidspace.org.uk/python/configobj.html

업데이트 됨 : 여기에서 최신 코드를 찾으 십시오 .

Debian / Ubuntu를 사용하는 경우 패키지 관리자를 사용하여이 모듈을 설치할 수 있습니다.

apt-get install python-configobj

사용 예 :

from configobj import ConfigObj

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

8

이 작업을 수행하는 가장 쉬운 방법은 제 생각에 파이썬의 CSV 파서를 사용하는 것입니다. 다음은이 접근 방식과 테스트 드라이버를 보여주는 읽기 / 쓰기 기능입니다. 값이 여러 줄로 허용되지 않는 경우 작동합니다. :)

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 csv일반적인 ConfigParser불만 을 해결하기 위해 모듈을 영리하게 사용합니다 . 더 쉽게 일반화되고 Python 2 및 3과 호환 되도록 만들어졌습니다 .
martineau 2013

6

이 문제를 직접 겪은 후, 허용 된 답변에 연결된 Alex Martelli의 접근 방식을 기반으로 섹션없이 투명하게 파일을 읽고 쓸 수있는 ConfigParser (Python 2 버전)에 완전한 래퍼를 작성했습니다. ConfigParser 사용에 대한 드롭 인 교체 여야합니다. 필요한 사람이이 페이지를 찾을 수 있도록 게시합니다.

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)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.