问题 如何ConfigParse文件保持相同键的多个值?


我需要能够使用 ConfigParser 读取同一个键的多个值。示例配置文件:

[test]
foo = value1
foo = value2
xxx = yyy

随着'标准'的使用 ConfigParser 会有一把钥匙 foo 有价值 value2。但我需要解析器读入两个值。

以下 复制键上的条目 我创建了以下示例代码:

from collections import OrderedDict
from ConfigParser import RawConfigParser

class OrderedMultisetDict(OrderedDict):
    def __setitem__(self, key, value):

        try:
            item = self.__getitem__(key)
        except KeyError:
            super(OrderedMultisetDict, self).__setitem__(key, value)
            return

        print "item: ", item, value
        if isinstance(value, list):
            item.extend(value)
        else:
            item.append(value)
        super(OrderedMultisetDict, self).__setitem__(key, item)


config = RawConfigParser(dict_type = OrderedDict)
config.read(["test.cfg"])
print config.get("test",  "foo")
print config.get("test",  "xxx")

config2 = RawConfigParser(dict_type = OrderedMultisetDict)
config2.read(["test.cfg"])
print config2.get("test",  "foo")
print config.get("test",  "xxx")

第一部分(有 config)在配置文件中读取'通常',只留下 value2 作为的价值 foo (覆盖/删除其他值)我得到以下预期输出:

value2
yyy

第二部分(config2)使用我的方法将多个值附加到列表,但输出是

['value1', 'value2', 'value1\nvalue2']
['yyy', 'yyy']

我如何摆脱重复的价值观?我期待输出如下:

['value1', 'value2']
yyy

要么

['value1', 'value2']
['yyy']

(我不介意每个值都在列表中......)。欢迎任何建议。


9639
2018-04-06 08:28


起源



答案:


经过一个小小的修改,我就能达到你想要的效果:

class MultiOrderedDict(OrderedDict):
    def __setitem__(self, key, value):
        if isinstance(value, list) and key in self:
            self[key].extend(value)
        else:
            super(MultiOrderedDict, self).__setitem__(key, value)
            # super().__setitem__(key, value) in Python 3

config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
config.read(['a.txt'])
print config.get("test",  "foo")
print config.get("test",  "xxx")

输出:

['value1', 'value2']
['yyy']

11
2018-04-06 08:57



我不知道为什么 super(OrderedDict, self) 有效,但没有 super(MultiOrderedDict, self)。 - Nathan Villaescusa
是的,完美!除了 super 异常。也许吧 是 的基础类 OrderedDict 然后... - Alex
当只有一个值时,有没有办法让它返回单个值而不是列表? ['value1','value2']和yyy而不是['yyy'] - Vangelis Tasoulas
你有办法在python 3中完成这项工作吗?我收到一个错误: configparser.DuplicateOptionError,我真的不知道自己在做什么。 - bgStack15
@ bgStack15你应该设置 strict 至 False, 喜欢这个: config = configparser.RawConfigParser(dict_type=MultiOrderedDict, strict=False) - Roberto


答案:


经过一个小小的修改,我就能达到你想要的效果:

class MultiOrderedDict(OrderedDict):
    def __setitem__(self, key, value):
        if isinstance(value, list) and key in self:
            self[key].extend(value)
        else:
            super(MultiOrderedDict, self).__setitem__(key, value)
            # super().__setitem__(key, value) in Python 3

config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
config.read(['a.txt'])
print config.get("test",  "foo")
print config.get("test",  "xxx")

输出:

['value1', 'value2']
['yyy']

11
2018-04-06 08:57



我不知道为什么 super(OrderedDict, self) 有效,但没有 super(MultiOrderedDict, self)。 - Nathan Villaescusa
是的,完美!除了 super 异常。也许吧 是 的基础类 OrderedDict 然后... - Alex
当只有一个值时,有没有办法让它返回单个值而不是列表? ['value1','value2']和yyy而不是['yyy'] - Vangelis Tasoulas
你有办法在python 3中完成这项工作吗?我收到一个错误: configparser.DuplicateOptionError,我真的不知道自己在做什么。 - bgStack15
@ bgStack15你应该设置 strict 至 False, 喜欢这个: config = configparser.RawConfigParser(dict_type=MultiOrderedDict, strict=False) - Roberto


接受的答案打破了 config.sections(),它总是返回一个空列表(使用Python 3.5.3测试)。更换 super(OrderedDict, self).__setitem__(key, value) 通过 super().__setitem__(key, value) 解决这个问题,但现在 config.get(section, key) 返回连接的字符串,不再是字符串列表。

我的解决方案是:

class ConfigParserMultiValues(collections.OrderedDict):

    def __setitem__(self, key, value):
        if key in self and isinstance(value, list):
            self[key].extend(value)
        else:
            super().__setitem__(key, value)

    @staticmethod
    def getlist(value):
        return value.split(os.linesep)

    config = configparser.ConfigParser(strict=False, empty_lines_in_values=False, dict_type=ConfigParserMultiValues, converters={"list": ConfigParserMultiValues.getlist})
    ...
    values = config.getlist("Section", "key") # => ["value1", "value2"]

config INI文件接受重复键:

[Section]
    key = value1
    key = value2

2
2017-08-15 09:11



我在接受的答案中修复了错误的super()调用,而section()现在可以正常工作。 - bernie


稍微改变一下 @ abarnert的回答,否则它递归地调用__setitem__,并且由于某种原因不会停止。

ini文件:

[section]
key1   = value1
key2[] = value21
key2[] = value22

蟒蛇:

class MultiOrderedDict(OrderedDict):
    LIST_SUFFIX = '[]'
    LIST_SUFFIX_LEN = len(LIST_SUFFIX)

    def __setitem__(self, key, value):
        if key.endswith(self.LIST_SUFFIX):
            values = super(OrderedDict, self).setdefault(key, [])
            if isinstance(value, list):
                values.extend(value)
            else:
                values.append(value)
        else:
            super(MultiOrderedDict, self).__setitem__(key, value)

    def __getitem__(self, key):
        value = super(MultiOrderedDict, self).__getitem__(key)
        if key.endswith(self.LIST_SUFFIX) and not isinstance(value, list):
            value = value.split('\n')
        return value

测试:

def test_ini(self):
    dir_path = os.path.dirname(os.path.realpath(__file__))
    config = RawConfigParser(dict_type=MultiOrderedDict, strict=False)
    config.readfp(codecs.open('{}/../config/sample.ini'.format(dir_path), encoding="utf_8_sig"))
    self.assertEquals(config.get("section1", "key1"), 'value1')
    self.assertEquals(config.get("section1", "key2[]"), ['value21', 'value22'])

-1
2018-05-08 06:11





更多示例中的多个值 test.cfg

[test]
foo = value1
foo = value2
 value3
xxx = yyy

<whitespace>value3 附加 value3 为了foo列表。

ConfigParser 将列表转换为字符串。

/usr/lib/python2.7/ConfigParser.pyc in _read(self, fp, fpname)
    552             for name, val in options.items():
    553                 if isinstance(val, list):
--> 554                     options[name] = '\n'.join(val)
    555 

value 在转换之前总是列表或字典(MultiOrderedDict)。

试试这个 - 用它, config.items 作品:

from collections import OrderedDict
import ConfigParser

class MultiOrderedDict(OrderedDict):
    def __setitem__(self, key, value):
        if key in self:
            if isinstance(value, list):
                self[key].extend(value)
                return
            elif isinstance(value,str):
                return # ignore conversion list to string (line 554)
        super(MultiOrderedDict, self).__setitem__(key, value)

config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
config.read(['test.cfg'])
print config.get("test",  "foo")
print config.get("test",  "xxx")
print config.items("test")

输出:

['value1', 'value2', 'value3']
['yyy']
[('foo', ['value1', 'value2', 'value3']), ('xxx', ['yyy'])]

另一个实现 MultiOrderedDict

class MultiOrderedDict(OrderedDict):
    def __setitem__(self, key, value):
        if key in self:
            if isinstance(value, list):
                self[key].extend(value)
                return
            elif isinstance(value,str):
                if len(self[key])>1:
                    return
        super(MultiOrderedDict, self).__setitem__(key, value)

输出:

['value1', 'value2', 'value3']
yyy
[('foo', ['value1', 'value2', 'value3']), ('xxx', 'yyy')]

-2
2017-07-09 21:35