python-3.x 使用ConfigParser更改INI文件后,是否可以保持INI文件的格式?

55ooxyrt  于 2023-01-22  发布在  Python
关注(0)|答案(1)|浏览(165)

ConfigParser是否可能保持INI配置文件的格式?我有一些配置文件,其中包含注解和特定的section/option名称,如果读取并更改文件的内容,ConfigParser会重新格式化它(我可以解决section/option名称)。
我熟悉ConfigParser的工作方式(将键/值对读取到dict并在更改后将其转储到文件)。但我感兴趣的是,是否有解决方案可以保持INI文件中的原始格式和注解。

    • 示例:**
    • 一米十纳一x**
# Comment line
; Other Comment line
[My-Section]
Test-option = Test-Variable
    • 一米十一米一**
import configparser as cp

parser: cp.ConfigParser = cp.ConfigParser()
parser.read("test.ini")

parser.set("My-Section", "New-Test_option", "TEST")

with open("test.ini", "w") as configfile:
    parser.write(configfile)
    • 运行脚本后test.ini**
[My-Section]
test-option = Test-Variable
new-test_option = TEST

正如你在上面看到的,注解行(两种类型的注解)都被删除了。此外,option名称也被重新格式化了。
如果我在源代码中添加以下代码行,那么我可以保留options的格式,但注解仍然会被删除:

parser.optionxform = lambda option: option

因此,test.ini文件在运行以上行的脚本后:

[My-Section]
Test-option = Test-Variable
New-Test_option = TEST
    • 所以我的问题:**
  • 修改INI文件后,是否可以保留其中的注解?
  • 是否可以保持文件的格式,例如:空格、制表符、新行等?
    • 注:**
  • 我已经检查了RawConfigParser模块,但正如我所看到的,它也不支持格式保持。
dxxyhpgq

dxxyhpgq1#

来自文档:
回写配置时,不会保留原始配置文件中的注解。

    • IF注解总是**在节名之前,您可以对文件进行预处理和后处理,以捕获注解并在更改后恢复它们。这有点像黑客,但可能比扩展configparser.ConfigParser更容易实现。

不考虑行内注解,尽管可以使用相同的方法来查找、关联和恢复行内注解。
使用正则表达式查找注解,然后将其与节关联。

import configparser as cp
import re

pattern = r'([#;][^[]*)(\[[^]]*\])'
rex = re.compile(pattern)
# {section_name:comment}
comments = {}
with open('test.ini') as f:
    text = f.read()
for comment,section in rex.findall(text):
    comments[section] = comment

使用ConfigParser进行更改。

parser: cp.ConfigParser = cp.ConfigParser()
parser.read("test.ini")

parser.set("My-Section", "New-Test_option", "TEST")

with open("test.ini", "w") as configfile:
    parser.write(configfile)

恢复注解。

with open('test.ini') as f:
    text = f.read()
for section,comment in comments.items():
    text = text.replace(section,comment+section)
with open('test.ini','w') as f:
    f.write(text)

test.ini

# comment line
; other comment line
[My-Section]
test-option = Test-Variable

; another pesky comment

[foo-section]
this-option = x

comments命令如下所示:

{'[My-Section]': '# comment line\n; other comment line\n',
 '[foo-section]': '; another pesky comment\n\n'}

更改后的test.ini

# comment line
; other comment line
[My-Section]
test-option = Test-Variable
new-test_option = TEST

; another pesky comment

[foo-section]
this-option = x

最后,这里是ConfigParser的一个子类,重写了_read和_write_section方法,以便查找/关联/恢复注解如果它们 * 出现 * 在节之前。

import configparser as cp
from configparser import *
import re
class ConfigParserExt(cp.ConfigParser):
    def _read(self, fp, fpname):
        """Parse a sectioned configuration file.
        Each section in a configuration file contains a header, indicated by
        a name in square brackets (`[]`), plus key/value options, indicated by
        `name` and `value` delimited with a specific substring (`=` or `:` by
        default).
        Values can span multiple lines, as long as they are indented deeper
        than the first line of the value. Depending on the parser's mode, blank
        lines may be treated as parts of multiline values or ignored.
        Configuration files may include comments, prefixed by specific
        characters (`#` and `;` by default). Comments may appear on their own
        in an otherwise empty line or may be entered in lines holding values or
        section names. Please note that comments get stripped off when reading configuration files;
        unless they are positioned just before sections
        """

        # find comments and associate with section
        try:
            text = fp.read()
            fp.seek(0)
        except AttributeError as e:
            text = ''.join(line for line in fp)
        rex = re.compile(r'([#;][^[]*)(\[[^]]*\])')
        self.preserved_comments = {}
        for comment,section in rex.findall(text):
            self.preserved_comments[section] = comment
        
        super()._read(fp,fpname)

    def _write_section(self, fp, section_name, section_items, delimiter):
        """Write a single section to the specified `fp`."""
        # restore comment to section
        if f'[{section_name}]' in self.preserved_comments:
            fp.write(self.preserved_comments[f'[{section_name}]'])
        super()._write_section( fp, section_name, section_items, delimiter)

相关问题