Python 配置解析器读取注释和值

发布于 2025-01-01 15:41:28 字数 478 浏览 1 评论 0原文

我有配置文件,

[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()

但我也想阅读当前的注释和值,这方面的任何建议都会非常有帮助。

I have config file,

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

code like this reads only value of the key:

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()

but i also want to read the comment present along with the value, any suggestions in this regards will be very helpful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

绅刃 2025-01-08 15:41:28

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

在您的具体情况下,这很容易,因为 # 仅在一行开始时充当注释字符。因此,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! 

Alas, this is not easily done in general case. Comments are supposed to be ignored by the parser.

In your specific case, it is easy, because # only serves as a comment character if it begins a line. So variable1's value will be "val1 #comment1". I suppose you use something like this, only less brittle:

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

If the value of a 'comment' is needed, probably it is not a proper comment? Consider adding explicit keys for the 'comments', like this:

[local]
  var1: 108.5j
  var1_comment: remember, the flux capacitor capacitance is imaginary! 
朦胧时间 2025-01-08 15:41:28

您唯一的解决方案是编写另一个 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'

Your only solutions is to write another ConfigParser overriding the method _read(). In your ConfigParser you should delete all checks about comment removal. This is a dangerous solution, but should work.

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)

In the ValuesWithCommentsConfigParser I fixed some imports and deleted the appropriate sections of code.

Using the same config.ini from my previous answer, I can prove the previous code is correct.

config = ValuesWithCommentsConfigParser()
config.read('config.ini')
assert config.get('local', 'variable1') == 'value1 ; comment1'
assert config.get('local', 'variable2') == 'value2 # comment2'
拥有 2025-01-08 15:41:28

根据 ConfigParser 模块 文档,

配置文件可能包含注释,以特定的前缀
字符(# 和 ;)。评论可能会单独出现在其他地方
空行,或者可以在包含值或部分的行中输入
名称。在后一种情况下,它们前面需要有一个空格
被识别为评论的字符。 (为了向后兼容,
仅有的 ;启动内联注释,而 # 则不启动。)

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

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

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

第一个解决方案要求您使用另一个密钥生成一个新密钥(即计算 variable1_comment from 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() 结果中的值和注释。

Accordiing to the ConfigParser module documentation,

Configuration files may include comments, prefixed by specific
characters (# and ;). Comments may appear on their own in an otherwise
empty line, or may be entered in lines holding values or section
names. In the latter case, they need to be preceded by a whitespace
character to be recognized as a comment. (For backwards compatibility,
only ; starts an inline comment, while # does not.)

If you want to read the "comment" with the value, you can omit the whitespace before the ; character or use the #. But in this case the strings comment1 and comment2 become part of the value and are not considered comments any more.

A better approach would be to use a different property name, such as variable1_comment, or to define another section in the configuration dedicated to comments:

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

The first solution requires you to generate a new key using another one (i.e. compute variable1_comment from variable1), the other one allows you to use the same key targeting different sections in the configuration file.

As of Python 2.7.2, is always possibile to read a comment along the line if you use the # character. As the docs say, it's for backward compatibility. The following code should run smoothly:

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

for the following config.ini file:

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

If you adopt this solution, remember to manually parse the result of get() for values and comments.

少女的英雄梦 2025-01-08 15:41:28

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

所以variable1的值是“val1 #comment1”。注释是值的一部分,

您可以检查您的配置是否在注释前输入了Enter键

according to the manuals:
Lines beginning with '#' or ';' are ignored and may be used to provide comments.

so the value of variable1 is "val1 #comment1".The comment is part of the value

you can check your config whether you put a Enter before your comment

七七 2025-01-08 15:41:28

万一后来有人过来。我的情况是我需要读取 Pascal 应用程序生成的 .ini 文件。该 configparser 不关心 # 或 ;启动按键。
例如,.ini 文件看起来像

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

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

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

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

In case anyone comes along afterwards. My situation was I needed to read in a .ini file generated by a Pascal Application. That configparser didn't care about # or ; starting the keys.
For example the .ini file would look like this

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

Python's configparser would skip that key value pair. Needed to modify the configparser to not look at # as comments

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

I'm sure I could set the comment_prefixes to whatever Pascal uses for comments, but didn't see any, so I set it to an empty string

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文