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