Python配置解析器(支持重复键)
最近我开始为我正在做的一个Python项目写一个配置解析器。起初我没有使用configparser和configobj,因为我想支持一种这样的配置文件:
key=value
key2=anothervalue
food=burger
food=hotdog
food=cake icecream
简单来说,这个配置文件会经常通过SSH在命令行上进行编辑。所以我不想太在意缩进或空格(像YAML那样),但我也想避免在vi中出现多个值的键被换行的情况(可能会有10个以上的值)。这就是我想支持重复键的原因。
在我理想的情况下,当我向Python的配置对象请求食物时,它会返回一个列表,比如['汉堡', '热狗', '蛋糕', '冰淇淋']。如果没有定义食物的值,它会去一个默认的配置文件中查找,并给我这些值。
我已经实现了上述功能
然而,我的问题开始于我意识到我想保留行内注释等内容。我处理读取和写入配置文件的方式是将文件解码成内存中的字典,从字典中读取值,或者将值写入字典,然后再把这个字典输出到文件中。这种方法并不太好,无法很好地保留行的顺序和注释,这让我很烦恼。
A) ConfigObj看起来有我需要的所有功能,除了支持重复键。它希望我创建一个列表,但在SSH下手动编辑这个列表会很麻烦,因为会换行。我可以让configobj更适合SSH/vi使用吗?
B) 我自己做的解决方案是否有问题?有没有更好的方法来读取/写入/存储我的配置值?有没有简单的方法可以通过修改某一行来改变配置文件中的键值,然后从内存中重新写入整个配置文件?
2 个回答
我肯定会尽量利用标准库里的东西,如果可以的话。
配置解析器类的定义看起来是这样的:
class ConfigParser.SafeConfigParser([defaults[, dict_type[, allow_no_value]]])
注意这里的 dict_type
参数。当你提供这个参数时,它会用来构建字典对象,这些字典对象会包含各个部分的列表、每个部分里的选项,以及默认值。默认情况下,它使用的是 collections.OrderedDict
。也许你可以在这里传入一些东西,以实现你想要的多键行为,然后就能享受到 ConfigParser
带来的所有好处。你可能需要自己写一个类来实现这个功能,或者你也可以在 PyPi 或 ActiveState 的食谱中找到别人写好的。试着找找“袋”或“多重集合”类。
我会选择这个方法,或者干脆忍耐一下,直接用列表:
foo = value1, value2, value3
一个疯狂的想法:把你的字典里的值做成一个包含三个元素的列表,每个元素包括行号、列号和实际的值,并且为注释添加一个特殊的键。
CommentSymbol = ';'
def readConfig(filename):
f = open(filename, 'r')
if not f:
return
def addValue(dict, key, lineIdx, colIdx, value):
if key in dict:
dict[key].append((lineIdx, colIdx, value))
else:
dict[key] = [(lineIdx, colIdx, value)]
res = {}
i = 0
for line in f.readlines():
idx = line.find(CommentSymbol)
if idx != -1:
comment = line[idx + 1:]
addValue(res, CommentSymbol, i, idx, comment)
line = line[:idx]
pair = [x.strip() for x in line.split('=')][:2]
if len(pair) == 2:
addValue(res, pair[0], i, 0, pair[1])
i += 1
return res
def writeConfig(dict, filename):
f = open(filename, 'w')
if not f:
return
index = sorted(dict.iteritems(), cmp = lambda x, y: cmp(x[1][:2], y[1][:2]))
i = 0
for k, V in index:
for v in V:
if v[0] > i:
f.write('\n' * (v[0] - i - 1))
if k == CommentSymbol:
f.write('{0}{1}'.format(CommentSymbol, str(v[2])))
else:
f.write('{0} = {1}'.format(str(k), str(v[2])))
i = v[0]
f.close()