Python-configparser更新write保留注释

背景

python语言用来解析配置文件的模块是ConfigParser,python3中是configparser模块,在使用中经常用到write方法将配置项重新写入文件:

config.ini文件:

# 数据库配置
[database]
# 主机
# IP
host = localhost
# 端口
port = 3306
# 用户名
username = my_user
# 密码
password = my_password

# 日志配置
[logging]
# 日志等级
level = debug
# 输出格式
output = log.txt

代码:

from configparser import ConfigParser

if __name__ == '__main__':
    # 创建ConfigParser对象
    config = ConfigParser()

    # 读取配置文件
    config.read('config.ini', encoding='utf-8')

    # 在配置文件中修改某个配置项的值
    config.set('database', 'port', '3307')  # 修改port为3307

    # 写入配置文件,保留原有注释
    with open('config.ini', 'w', encoding='utf-8') as configfile:
        config.write(configfile)

 结果:

[database]
host = localhost
port = 3307
username = my_user
password = my_password

[logging]
level = debug
output = log.txt

结果发现配置文件中的空行和注释行都会被去掉,虽然这个并不影响使用,但配置文件的可读性无疑还是变差了。 

解决办法

为此特地对ConfigParser模块进行了一点改动,使其保留注释项和空行。

思路

就是在读配置文件的时候碰到注释行或换行就缓存起来,然后在写入的时候从缓存中取出就可以了。

实现代码:

import os
from configparser import ConfigParser
from configparser import DEFAULTSECT


class MyConfigParser(ConfigParser):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.comment_line_dict = {}

    def _read_comments(self, fp):
        """
        read comments
        """
        # comment or blank line temp cache
        comment_line_cache = []
        sections = []
        for _, line in enumerate(fp):
            # comment or blank line?
            if line.strip() == '' or line[0] in self._comment_prefixes:
                comment_line_cache.append(line.strip())
                continue
            value = line.strip()

            # is it a section header?
            mo = self.SECTCRE.match(value)
            if mo:
                section_name = mo.group('header')
                self.comment_line_dict[section_name] = comment_line_cache
                comment_line_cache = []
                sections.append(section_name)
            # an option line?
            else:
                mo = self._optcre.match(value)
                if mo:
                    optname, vi, optval = mo.group('option', 'vi', 'value')
                    optname = self.optionxform(optname.rstrip())
                    self.comment_line_dict["%s.%s" % (sections[-1], optname)] = comment_line_cache
                    comment_line_cache = []

    def read(self, filenames, encoding=None):
        """
        Rewrite the read method of the parent class
        """
        if isinstance(filenames, (str, bytes, os.PathLike)):
            filenames = [filenames]
        read_ok = []
        for filename in filenames:
            try:
                with open(filename, encoding=encoding) as fp:
                    self._read(fp, filename)
                # Add methods for reading comments
                with open(filename, encoding=encoding) as fp:
                    self._read_comments(fp)
            except OSError:
                continue
            if isinstance(filename, os.PathLike):
                filename = os.fspath(filename)
            read_ok.append(filename)
        return read_ok

    def write(self, fp, space_around_delimiters=True):
        """Write an .ini-format representation of the configuration state."""
        if self._defaults:
            comment_line = self.comment_line_dict.get("%s" % (DEFAULTSECT), [])
            if comment_line:
                fp.write("\n".join(comment_line) + "\n")
            fp.write("[%s]\n" % DEFAULTSECT)
            for (key, value) in self._defaults.items():
                comment_line = self.comment_line_dict.get("%s.%s" % (DEFAULTSECT, key), [])
                if comment_line:
                    fp.write("\n".join(comment_line) + "\n")
                fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
            fp.write("\n")
        for section in self._sections:
            comment_line = self.comment_line_dict.get("%s" % (section), [])
            if comment_line:
                fp.write("\n".join(comment_line) + "\n")
            fp.write("[%s]\n" % section)
            for (key, value) in self._sections[section].items():
                comment_line = self.comment_line_dict.get("%s.%s" % (section, key), [])
                if comment_line:
                    fp.write("\n".join(comment_line) + "\n")
                if (value is not None) or (self._optcre == self.OPTCRE):
                    key = " = ".join((key, str(value).replace('\n', '\n\t')))
                fp.write("%s\n" % (key))
        fp.write("\n")


if __name__ == '__main__':
    # 创建ConfigParser对象
    # config = ConfigParser()
    config = MyConfigParser()

    # 读取配置文件
    config.read('config.ini', encoding='utf-8')

    # 在配置文件中修改某个配置项的值
    config.set('database', 'port', '3307')  # 修改port为3307

    # 写入配置文件,保留原有注释
    with open('config.ini', 'w', encoding='utf-8') as configfile:
        config.write(configfile)

结果:

# 数据库配置
[database]
# 主机
# IP
host = localhost
# 端口
port = 3307
# 用户名
username = my_user
# 密码
password = my_password

# 日志配置
[logging]
# 日志等级
level = debug
# 输出格式
output = log.txt

实现的可能不太严谨,大家根据需要可以修改实现更有扩展性的功能。


参考:

Python 使用ConfigParser写入注释到文件中|极客教程

保留注释换行的python模块configparser 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值