You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python3中读写INI配置文件且保留注释?

嘿,这个问题我之前在项目里也遇到过!Python标准库自带的configparser默认会丢弃INI文件里的注释和原始格式,导致写回文件时注释全没了。不过有两个靠谱的解决方案,看你的需求选:

方案1:用第三方库configobj(推荐)

这个库就是专门为处理带注释的INI文件设计的,原生支持保留注释、空行、缩进和原始文件结构,比标准库的configparser更适合这种场景。

首先安装它:

pip install configobj

然后使用起来很简单:

from configobj import ConfigObj

# 读取配置文件,自动保留所有注释和格式
cfg = ConfigObj('your_config.ini', encoding='utf-8', indent_type='    ')  # indent_type可以指定缩进方式,比如空格或制表符

# 修改配置(和操作字典一样方便)
cfg['DEFAULT']['parameter'] = 8

# 写回文件,注释完全保留
cfg.write()

它能处理各种复杂情况,比如行内注释、多行值、不同的注释符号,完全不用你操心格式丢失的问题。

方案2:自定义ConfigParser类(不依赖第三方库)

如果不想引入第三方依赖,你可以自己扩展标准库的ConfigParser,在读取时记录注释的位置和内容,写回时再把注释插回去。下面是一个简化版的实现,适合大多数简单的INI文件场景:

import configparser
from collections import defaultdict

class CommentedConfigParser(configparser.ConfigParser):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 存储section前的注释,和每个key前的注释
        self.section_comments = defaultdict(list)
        self.key_comments = defaultdict(dict)

    def read(self, filenames, encoding=None):
        # 手动读取解析,记录注释
        for filename in filenames if isinstance(filenames, list) else [filenames]:
            with open(filename, 'r', encoding=encoding) as f:
                current_section = None
                current_comments = []
                for line in f:
                    stripped_line = line.strip()
                    # 跳过空行
                    if not stripped_line:
                        continue
                    # 处理注释行
                    if stripped_line.startswith(('#', ';')):
                        current_comments.append(line.rstrip('\n'))
                        continue
                    # 处理section
                    if stripped_line.startswith('[') and stripped_line.endswith(']'):
                        current_section = stripped_line[1:-1].strip()
                        self.section_comments[current_section] = current_comments
                        current_comments = []
                        if not self.has_section(current_section):
                            self.add_section(current_section)
                        continue
                    # 处理键值对
                    if '=' in stripped_line:
                        key, value = stripped_line.split('=', 1)
                        key = key.strip()
                        value = value.strip()
                        self.set(current_section, key, value)
                        self.key_comments[current_section][key] = current_comments
                        current_comments = []
                        continue

    def write(self, fileobject):
        # 写回时先处理DEFAULT section
        if 'DEFAULT' in self.sections():
            # 写section前的注释
            if self.section_comments['DEFAULT']:
                fileobject.write('\n'.join(self.section_comments['DEFAULT']) + '\n')
            fileobject.write('[DEFAULT]\n')
            # 写每个key和对应的注释
            for key, value in self['DEFAULT'].items():
                if key in self.key_comments['DEFAULT']:
                    fileobject.write('\n'.join(self.key_comments['DEFAULT'][key]) + '\n')
                fileobject.write(f'{key} = {value}\n')
            fileobject.write('\n')
        # 处理其他sections
        for section in self.sections():
            if section == 'DEFAULT':
                continue
            if self.section_comments[section]:
                fileobject.write('\n'.join(self.section_comments[section]) + '\n')
            fileobject.write(f'[{section}]\n')
            for key, value in self[section].items():
                if key in self.key_comments[section]:
                    fileobject.write('\n'.join(self.key_comments[section][key]) + '\n')
                fileobject.write(f'{key} = {value}\n')
            fileobject.write('\n')

使用这个自定义类的方式和标准库差不多:

# 读取配置
cfg = CommentedConfigParser()
cfg.read('your_config.ini')

# 修改配置
cfg.set('DEFAULT', 'parameter', '8')

# 写回文件
with open('your_config.ini', 'w') as f:
    cfg.write(f)

注意:这个自定义类是简化实现,可能处理不了行内注释、多行值等复杂情况,如果你的INI文件结构复杂,还是推荐用configobj更稳妥。

内容的提问来源于stack exchange,提问作者buhtz

火山引擎 最新活动