Использование Python ConfigParser для чтения файла без имени раздела - PullRequest
75 голосов
/ 22 мая 2010

Я использую ConfigParser для чтения конфигурации скрипта во время выполнения.

Я бы хотел иметь возможность не указывать имя раздела (есть сценарии, которые достаточно просты, им не нужен «раздел») ConfigParser сгенерирует исключение NoSectionError и не примет файл.

Как я могу заставить ConfigParser просто получать (key, value) кортежи файла конфигурации без имен разделов? Например:

key1=val1
key2:val2

Я бы не хотел писать в файл конфигурации.

Ответы [ 7 ]

45 голосов
/ 22 мая 2010

Алекс Мартелли предоставил решение для использования ConfigParser для анализа .properties файлов (которые, очевидно, являются файлами конфигурации без сечения).

Его решение представляет собой файлоподобную оболочку, которая автоматически вставляет фиктивный заголовок раздела для удовлетворения требований ConfigParser.

40 голосов
/ 25 мая 2012

Просветленный этот ответ от 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)


РЕДАКТИРОВАТЬ для будущих googlers: Начиная с Python 3.4+ readfp устарело, и StringIO больше не требуется. Вместо этого мы можем использовать read_string напрямую:

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

config_parser = RawConfigParser()
config_parser.read_string(file_content)
32 голосов
/ 11 ноября 2014

Вы можете сделать это в одной строке кода.

В 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').

Вы также можете использовать StringIO в Python 3, возможно, для совместимости как со старыми, так и с новыми интерпретаторами Python, но обратите внимание, что теперь он находится в пакете io и readfp() теперь устарела.

В качестве альтернативы, вы можете рассмотреть возможность использования TOML парсера вместо ConfigParser.

16 голосов
/ 27 октября 2011

Вы можете использовать библиотеку 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
7 голосов
/ 23 октября 2012

Самый простой способ сделать это, по моему мнению, использовать CSV-парсер Python. Вот функция чтения / записи, демонстрирующая этот подход, а также тестовый драйвер. Это должно работать при условии, что значения не могут быть многострочными. :)

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() 
6 голосов
/ 06 февраля 2012

Сам столкнувшись с этой проблемой, я написал полную оболочку для 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)
5 голосов
/ 02 апреля 2015

В ответе Blueicefield упоминается configobj, но исходная библиотека поддерживает только Python 2. Теперь у нее есть порт, совместимый с Python 3+:

https://github.com/DiffSK/configobj

API-интерфейсы не изменились, см. документ .

...