保留注释换行的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] = '\n'.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("\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")
完结。
python3和这个思路一样,只是代码结构有较大改动,这里就不粘贴代码了,想看的可以去这里: