8

我有配置文件,

[local]
    variable1 : val1 ;#comment1
    variable2 : val2 ;#comment2

像这样的代码只读取键的值:

class Config(object):
    def __init__(self):
        self.config = ConfigParser.ConfigParser()
        self.config.read('config.py')

    def get_path(self):
        return self.config.get('local', 'variable1')

if __name__ == '__main__':
    c = Config()
    print c.get_path()

但我也想阅读与价值一起出现的评论,这方面的任何建议都会非常有帮助。

4

5 回答 5

8

唉,这在一般情况下并不容易做到。解析器应该忽略注释。

在您的特定情况下,这很容易,因为#只有在开始一行时才用作注释字符。所以variable1的值将是"val1 #comment1"。我想你使用这样的东西,只是不那么脆弱:

val1_line = c.get('local', 'var1')
val1, comment = val1_line.split(' #') 

如果需要“评论”的值,可能它不是正确的评论?考虑为“评论”添加显式键,如下所示:

[local]
  var1: 108.5j
  var1_comment: remember, the flux capacitor capacitance is imaginary! 
于 2012-02-02T10:16:05.213 回答
2

根据ConfigParser 模块文档,

配置文件可能包含注释,前缀为特定字符(# 和 ;)。注释可以单独出现在空行中,也可以在包含值或部分名称的行中输入。在后一种情况下,它们前面需要一个空格字符才能被识别为注释。(为了向后兼容,只有 ; 开始一个内联注释,而 # 没有。)

如果要读取带有值的“注释”,可以省略字符前的空格;或使用#. 但在这种情况下,字符串comment1comment2成为值的一部分,不再被视为注释。

更好的方法是使用不同的属性名称,例如variable1_comment,或者在配置中定义另一个专门用于注释的部分:

[local]
    variable1 = value1
[comments]
    variable1 = comment1

第一个解决方案要求您使用另一个(即计算variable1_comment来源variable1)生成一个新密钥,另一个允许您使用针对配置文件中不同部分的相同密钥。

从 Python 2.7.2开始,如果您使用该字符,则始终可以沿行阅读注释。#正如文档所说,它是为了向后兼容。以下代码应该可以顺利运行:

config = ConfigParser.ConfigParser()
config.read('config.ini')
assert config.get('local', 'variable1') == 'value1'
assert config.get('local', 'variable2') == 'value2 # comment2'

对于以下config.ini文件:

[local]
variable1 = value1 ; comment1
variable2 = value2 # comment2

如果您采用此解决方案,请记住手动解析get()for values 和 comments 的结果。

于 2012-02-02T10:24:05.287 回答
2

您唯一的解决方案是编写另一个ConfigParser覆盖方法_read()。在你的ConfigParser你应该删除所有关于评论删除的检查。这是一个危险的解决方案,但应该有效。

class ValuesWithCommentsConfigParser(ConfigParser.ConfigParser):

    def _read(self, fp, fpname):
        from ConfigParser import DEFAULTSECT, MissingSectionHeaderError, ParsingError

        cursect = None                        # None, or a dictionary
        optname = None
        lineno = 0
        e = None                              # None, or an exception
        while True:
            line = fp.readline()
            if not line:
                break
            lineno = lineno + 1
            # comment or blank line?
            if line.strip() == '' or line[0] in '#;':
                continue
            if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
                # no leading whitespace
                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')
                    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())
                        # This check is fine because the OPTCRE cannot
                        # match if it would set optval to None
                        if optval is not None:
                            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)

ValuesWithCommentsConfigParser我修复了一些导入并删除了相应的代码部分。

使用与config.ini之前的答案相同的方法,我可以证明之前的代码是正确的。

config = ValuesWithCommentsConfigParser()
config.read('config.ini')
assert config.get('local', 'variable1') == 'value1 ; comment1'
assert config.get('local', 'variable2') == 'value2 # comment2'
于 2012-02-02T11:40:55.087 回答
1

根据手册:以'#'或';'开头的行 被忽略,可用于提供评论。

所以变量1的值是“val1 #comment1”。注释是值的一部分

您可以检查您的配置是否在评论前输入了 Enter

于 2012-02-02T10:27:58.247 回答
0

以防后面有人过来。我的情况是我需要读取由 Pascal 应用程序生成的 .ini 文件。那个 configparser 不关心 # 或 ; 启动钥匙。例如,.ini 文件看起来像这样

  [KEYTYPE PATTERNS]
  ##-######=CLAIM

Python 的 configparser 会跳过该键值对。需要修改 configparser 以不将 # 视为注释

  config = configparser.ConfigParser(comment_prefixes="")
  config.read("thefile")

我确定我可以将 comment_prefixes 设置为 Pascal 用于评论的任何内容,但没有看到任何内容,所以我将其设置为空字符串

于 2021-02-18T22:52:10.287 回答