我是Python新手,我需要修改配置文件。我可以使用 configparser 来做到这一点,但问题是我需要将注释保留在配置文件中,而且似乎这是不可能的。我找到了更新INI文件而不删除注释,但问题并没有完全解决。
明显的解决方案是使用
ConfigObj
但评论说它已经过时了,所以我不想使用这种方法。
如果您在使用 ConfigParser 解析之前预处理您的配置文件,并对 ConfigParser 写入的输出进行后处理,那么您可以更改 INI 文件而不删除注释。
我建议在预处理过程中将每个评论转换为一个选项(键/值对)。那么 ConfigParser 就不会抛出注释了。在后期处理过程中,您可以“解压”评论并恢复它。
为了简化过程,您可能需要子类化 ConfigParser 并重写 _read 和 write 方法。
我已经完成了此操作并在此要点中发布了 CommentConfigParser 类。它有一个限制。它不支持缩进的节标题、注释和键。它们不应该有前导空格。
class CommentConfigParser(configparser.ConfigParser):
"""Comment preserving ConfigParser.
Limitation: No support for indenting section headers,
comments and keys. They should have no leading whitespace.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Backup _comment_prefixes
self._comment_prefixes_backup = self._comment_prefixes
# Unset _comment_prefixes so comments won't be skipped
self._comment_prefixes = ()
# Template to store comments as key value pair
self._comment_template = "#{0} = {1}"
# Regex to match the comment id prefix
self._comment_regex = re.compile(r"^#\d+\s*=\s*")
# List to store comments above the first section
self._top_comments = []
def _read(self, fp, fpname):
lines = fp.readlines()
above_first_section = True
# Preprocess config file to preserve comments
for i, line in enumerate(lines):
if line.startswith("["):
above_first_section = False
elif line.startswith(self._comment_prefixes_backup):
if above_first_section:
# Remove this line for now
lines[i] = ""
self._top_comments.append(line)
else:
# Store comment as value with unique key based on line number
lines[i] = self._comment_template.format(i, line)
# Feed the preprocessed file to the original _read method
return super()._read(io.StringIO("".join(lines)), fpname)
def write(self, fp, space_around_delimiters=True):
# Write the config to an in-memory file
with io.StringIO() as sfile:
super().write(sfile, space_around_delimiters)
# Start from the beginning of sfile
sfile.seek(0)
lines = sfile.readlines()
for i, line in enumerate(lines):
# Remove the comment id prefix
lines[i] = self._comment_regex.sub("", line, 1)
fp.write("".join(self._top_comments + lines))