保留註釋換行的python模塊configparser

python語言用來解析配置文件的模塊是ConfigParser,python3中是configparser模塊,我在使用中發現write方法在將配置項從新寫入文python

件時,配置文件中的空行和註釋行都會被去掉,雖然這個並不影響使用,但配置文件的可讀性無疑仍是變差了,爲此特意對ConfigParser模塊進git

行了一點改動,使其保留註釋項和空行。github

代碼很簡單。思路就是在讀配置文件的時候碰到註釋行或換行就緩存起來,而後在寫入的時候從緩存中取出就能夠了。緩存

以python2爲例,上代碼:app

一、修改 RawConfigParser 類定義,增長一個用於緩存註釋行和空行的字典less

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 = {}

二、修改_read方法,緩存註釋行和空行spa

    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] = '\n'.join(val)

 三、修改write方法,將保存的註釋行和空行寫入文件code

    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("\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():
                if key == "__name__":
                    continue
                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")

 完結。orm

python3和這個思路同樣,只是代碼結構有較大改動,這裏就不粘貼代碼了,想看的能夠去這裏:blog

https://github.com/duanyifei/configparser

相關文章
相關標籤/搜索