【问题标题】:parsing .properties file in Python在 Python 中解析 .properties 文件
【发布时间】:2011-02-18 16:00:57
【问题描述】:

ConfigParser 模块在解析一个简单的 Java 风格 .properties 文件时引发异常,该文件的内容是键值对(即没有 INI 风格的节标题) .有什么解决办法吗?

【问题讨论】:

标签: python key-value ini


【解决方案1】:

说你有,例如:

$ cat my.props
first: primo
second: secondo
third: terzo

即将是 .config 格式,只是它缺少前导部分名称。然后,很容易伪造节标题:

import ConfigParser

class FakeSecHead(object):
    def __init__(self, fp):
        self.fp = fp
        self.sechead = '[asection]\n'

    def readline(self):
        if self.sechead:
            try: 
                return self.sechead
            finally: 
                self.sechead = None
        else: 
            return self.fp.readline()

用法:

cp = ConfigParser.SafeConfigParser()
cp.readfp(FakeSecHead(open('my.props')))
print cp.items('asection')

输出:

[('second', 'secondo'), ('third', 'terzo'), ('first', 'primo')]

【讨论】:

  • 如果configparser 中有一个选项可以抑制该异常,那会很棒,为了像我这样的凡人:)
  • 很好的解决方案,但可以缩短很多:def FakeSecHead(fp): yield '[asection]\n'; yield from fp
  • 写入配置文件也可以吗?
【解决方案2】:

Alex Martelli 上面的回答不适用于 Python 3.2+:readfp() 已被 read_file() 替换,现在它需要一个迭代器,而不是使用 readline() 方法。

这是一个使用相同方法的 sn-p,但适用于 Python 3.2+。

>>> import configparser
>>> def add_section_header(properties_file, header_name):
...   # configparser.ConfigParser requires at least one section header in a properties file.
...   # Our properties file doesn't have one, so add a header to it on the fly.
...   yield '[{}]\n'.format(header_name)
...   for line in properties_file:
...     yield line
...
>>> file = open('my.props', encoding="utf_8")
>>> config = configparser.ConfigParser()
>>> config.read_file(add_section_header(file, 'asection'), source='my.props')
>>> config['asection']['first']
'primo'
>>> dict(config['asection'])
{'second': 'secondo', 'third': 'terzo', 'first': 'primo'}
>>>

【讨论】:

  • Python 3.2 还添加了read_string(),这使得附加虚拟部分成为一项微不足道的任务。
  • add_section_header 可以简单地是:config.read_file(itertools.chain(['[SECTION_NAME]'], file))
【解决方案3】:

我的解决方案是使用StringIO 并预先添加一个简单的虚拟标头:

import StringIO
import os
config = StringIO.StringIO()
config.write('[dummysection]\n')
config.write(open('myrealconfig.ini').read())
config.seek(0, os.SEEK_SET)

import ConfigParser
cp = ConfigParser.ConfigParser()
cp.readfp(config)
somevalue = cp.getint('dummysection', 'somevalue')

【讨论】:

  • open() 调用中添加了所需的\n 并删除了不必要的'r' 模式。
  • 我发现这是最简单的解决方案。
  • 我的 ini 中有换行符,如何处理?即:一个设置有几个条目,一个在它自己的行上。
  • 这是一个不错的快速解决方案,但请注意,Java 属性文件可能会使用可能破坏 ConfigParser 的功能,例如,! 作为注释或 \(反斜杠)用于行继续和转义(其中其他)。可以在此处找到有关此类功能的更多详细信息:docs.oracle.com/javase/7/docs/api/java/util/Properties.html.
【解决方案4】:
with open('mykeyvaluepairs.properties') as f:
    defaults = dict([line.split() for line in f])
config = configparser.ConfigParser(defaults)
config.add_section('dummy_section')

现在config.get('dummy_section', option) 将从默认部分返回“选项”。

或:

with open('mykeyvaluepairs.properties') as f:
    properties = dict([line.split() for line in f])
config = configparser.ConfigParser()
config.add_section('properties')
for prop, val in properties.items():
    config.set('properties', prop, val)

在这种情况下,config.get('properties', option) 不会使用默认部分。

【讨论】:

    【解决方案5】:

    我认为MestreLion's "read_string" comment 很好很简单,值得一个例子。

    对于 Python 3.2+,您可以像这样实现“虚拟部分”的想法:

    with open(CONFIG_PATH, 'r') as f:
        config_string = '[dummy_section]\n' + f.read()
    config = configparser.ConfigParser()
    config.read_string(config_string)
    

    【讨论】:

    • 优雅。除了您需要确保存在 CONFIG_PATH(也称为配置文件)这一事实之外。哪些 configparsers 内置为你做。但我想这只是一个尝试;-)
    【解决方案6】:

    耶!另一个版本

    基于this answer(添加使用dictwith语句,并支持%字符)

    import ConfigParser
    import StringIO
    import os
    
    def read_properties_file(file_path):
        with open(file_path) as f:
            config = StringIO.StringIO()
            config.write('[dummy_section]\n')
            config.write(f.read().replace('%', '%%'))
            config.seek(0, os.SEEK_SET)
    
            cp = ConfigParser.SafeConfigParser()
            cp.readfp(config)
    
            return dict(cp.items('dummy_section'))
    

    用法

    props = read_properties_file('/tmp/database.properties')
    
    # It will raise if `name` is not in the properties file
    name = props['name']
    
    # And if you deal with optional settings, use:
    connection_string = props.get('connection-string')
    password = props.get('password')
    
    print name, connection_string, password
    

    我的示例中使用的.properties 文件

    name=mongo
    connection-string=mongodb://...
    password=my-password%1234
    

    编辑 2015-11-06

    感谢Neill Lima 提到% 字符存在问题。

    原因是 ConfigParser 旨在解析 .ini 文件。 % 字符是一种特殊语法。为了使用% 字符,只需根据.ini 语法将% 替换为%%

    【讨论】:

    • 这个解决方案对我来说效果很好,直到我有一个带 '%' 的密码 - 没有单引号,它使 ConfigParser 崩溃。 Python 2.7.6
    • 您的解决方案对我有用,但我必须通过额外的.replace('\\\n', '')) 来适应多行基本上任何额外的条件都可以使用write... replace
    【解决方案7】:

    This answer 建议在 Python 3 中使用 itertools.chain。

    from configparser import ConfigParser
    from itertools import chain
    
    parser = ConfigParser()
    with open("foo.conf") as lines:
        lines = chain(("[dummysection]",), lines)  # This line does the trick.
        parser.read_file(lines)
    

    【讨论】:

      【解决方案8】:

      基于Alex Martelli's answer的python2.7的另一个答案

      import ConfigParser
      
      class PropertiesParser(object):
      
          """Parse a java like properties file
      
          Parser wrapping around ConfigParser allowing reading of java like
          properties file. Based on stackoverflow example:
          https://stackoverflow.com/questions/2819696/parsing-properties-file-in-python/2819788#2819788
      
          Example usage
          -------------
          >>> pp = PropertiesParser()
          >>> props = pp.parse('/home/kola/configfiles/dev/application.properties')
          >>> print props
      
          """
      
          def __init__(self):
              self.secheadname = 'fakeSectionHead'
              self.sechead = '[' + self.secheadname + ']\n'
      
          def readline(self):
              if self.sechead:
                  try:
                      return self.sechead
                  finally:
                      self.sechead = None
              else:
                  return self.fp.readline()
      
          def parse(self, filepath):
              self.fp = open(filepath)
              cp = ConfigParser.SafeConfigParser()
              cp.readfp(self)
              self.fp.close()
              return cp.items(self.secheadname)
      

      【讨论】:

        【解决方案9】:
        with open('some.properties') as file:
            props = dict(line.strip().split('=', 1) for line in file)
        

        感谢How to create a dictionary that contains key‐value pairs from a text file

        如果值中有等号,maxsplit=1 很重要(例如someUrl=https://some.site.com/endpoint?id=some-value&someotherkey=value

        【讨论】:

        • 如果文件有 cmets,这将出错。我们可以避免使用这个:dict(line.strip().split('=', 1) for line in file if not line.startswith("#") and not len(line.strip()) == 0)
        【解决方案10】:
        from pyjavaproperties import Properties
        p = Properties()
        p.load(open('test.properties'))
        p.list()
        print p
        print p.items()
        print p['name3']
        p['name3'] = 'changed = value'
        print p['name3']
        p['new key'] = 'new value'
        p.store(open('test2.properties','w'))
        

        【讨论】:

        • 唯一使用为 Java 属性文件制作的实际库的人。与其他人相比,我将使用它并恢复它的易用性。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-07
        • 2018-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多