zoukankan      html  css  js  c++  java
  • 保留注释换行的python模块configparser

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

    件时,配置文件中的空行和注释行都会被去掉,虽然这个并不影响使用,但配置文件的可读性无疑还是变差了,为此特地对ConfigParser模块进

    行了一点改动,使其保留注释项和空行。

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

    以python2为例,上代码:

    1、修改 RawConfigParser 类定义,增加一个用于缓存注释行和空行的字典

    class RawConfigParser:
        def __init__(self, defaults=None, dict_type=_default_dict,
                     allow_no_value=False):
            ... # 省略代码
            
            # comment or blank line temp cache
            self.comment_line_dict = {}

    2、修改_read方法,缓存注释行和空行

        def _read(self, fp, fpname):
            """Parse a sectioned setup file.
    
            The sections in setup file contains a title line at the top,
            indicated by a name in square brackets (`[]'), plus key/value
            options lines, indicated by `name: value' format lines.
            Continuations are represented by an embedded newline then
            leading whitespace.  Blank lines, lines beginning with a '#',
            and just about everything else are ignored.
            """
            cursect = None                        # None, or a dictionary
            optname = None
            lineno = 0
            e = None                              # None, or an exception
            comment_line_cache = []               # comment or blank line temp cache
            while True:
                line = fp.readline()
                if not line:
                    break
                lineno = lineno + 1
                # comment or blank line?
                if line.strip() == '' or line[0] in '#;':
                    comment_line_cache.append(line.strip())
                    continue
                if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
                    # no leading whitespace
                    comment_line_cache.append(line.strip())
                    continue
                # continuation line?
                if line[0].isspace() and cursect is not None and optname:
                    value = line.strip()
                    if value:
                        cursect[optname].append(value)
                # a section header or option header?
                else:
                    # is it a section header?
                    mo = self.SECTCRE.match(line)
                    if mo:
                        sectname = mo.group('header')
                        self.comment_line_dict[sectname] = comment_line_cache
                        comment_line_cache = []
                        if sectname in self._sections:
                            cursect = self._sections[sectname]
                        elif sectname == DEFAULTSECT:
                            cursect = self._defaults
                        else:
                            cursect = self._dict()
                            cursect['__name__'] = sectname
                            self._sections[sectname] = cursect
                        # So sections can't start with a continuation line
                        optname = None
                    # no section header in the file?
                    elif cursect is None:
                        raise MissingSectionHeaderError(fpname, lineno, line)
                    # an option line?
                    else:
                        mo = self._optcre.match(line)
                        if mo:
                            optname, vi, optval = mo.group('option', 'vi', 'value')
                            optname = self.optionxform(optname.rstrip())
                            self.comment_line_dict["%s.%s"%(cursect['__name__'], optname)] = comment_line_cache
                            comment_line_cache = []                        
                            # This check is fine because the OPTCRE cannot
                            # match if it would set optval to None
                            if optval is not None:
                                if vi in ('=', ':') and ';' in optval:
                                    # ';' is a comment delimiter only if it follows
                                    # a spacing character
                                    pos = optval.find(';')
                                    if pos != -1 and optval[pos-1].isspace():
                                        optval = optval[:pos]
                                optval = optval.strip()
                                # allow empty values
                                if optval == '""':
                                    optval = ''
                                cursect[optname] = [optval]
                            else:
                                # valueless option handling
                                cursect[optname] = optval
                        else:
                            # a non-fatal parsing error occurred.  set up the
                            # exception but keep going. the exception will be
                            # raised at the end of the file and will contain a
                            # list of all bogus lines
                            if not e:
                                e = ParsingError(fpname)
                            e.append(lineno, repr(line))
            # if any parsing errors occurred, raise an exception
            if e:
                raise e
    
            # join the multi-line values collected while reading
            all_sections = [self._defaults]
            all_sections.extend(self._sections.values())
            for options in all_sections:
                for name, val in options.items():
                    if isinstance(val, list):
                        options[name] = '
    '.join(val)

     3、修改write方法,将保存的注释行和空行写入文件

        def write(self, fp):
            """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("
    ".join(comment_line) + "
    ")
                fp.write("[%s]
    " % DEFAULTSECT)
                for (key, value) in self._defaults.items():
                    comment_line = self.comment_line_dict.get("%s.%s"%(DEFAULTSECT, key), [])
                    if comment_line:
                        fp.write("
    ".join(comment_line) + "
    ")
                    fp.write("%s = %s
    " % (key, str(value).replace('
    ', '
    	')))
                fp.write("
    ")
            for section in self._sections:
                comment_line = self.comment_line_dict.get("%s"%(section), [])
                if comment_line:
                    fp.write("
    ".join(comment_line) + "
    ")            
                fp.write("[%s]
    " % section)
                for (key, value) in self._sections[section].items():
                    if key == "__name__":
                        continue
                    comment_line = self.comment_line_dict.get("%s.%s"%(section, key), [])
                    if comment_line:
                        fp.write("
    ".join(comment_line) + "
    ")
                    if (value is not None) or (self._optcre == self.OPTCRE):
                        key = " = ".join((key, str(value).replace('
    ', '
    	')))
                    fp.write("%s
    " % (key))
                fp.write("
    ")

     完结。

    python3和这个思路一样,只是代码结构有较大改动,这里就不粘贴代码了,想看的可以去这里:

    https://github.com/duanyifei/configparser

  • 相关阅读:
    tinyshop框架前后台操作基础教程
    Struts2与SpringMVC的区别
    2.spring源码-BeanPostProcessor后置处理之ApplicationContextAwareProcessor,实现spring容器中某一个类的bean对象在初始化时需要得到Spring容器内容。
    1.spring源码-BeanPostProcessor后置处理器
    3.获取某天的最大时间和最小时间,使用Calendar
    java生成验证码
    一次m2eclipse的安装大坑经历之http://m2eclipse.sonatype.org/sites/m2e
    Eclipse 安装 Maven 插件的几种方法
    3.取出每个班级分数排在前两位的学生
    2.js将Date对象转换成“2018-05-10”字符串格式化的时间
  • 原文地址:https://www.cnblogs.com/dyfblog/p/6722910.html
Copyright © 2011-2022 走看看