【问题标题】:Python: Read configuration file with multiple lines per keyPython:读取每个键多行的配置文件
【发布时间】:2011-01-15 18:56:29
【问题描述】:

我正在编写一个小型数据库测试套件,它会读取带有查询和预期结果的配置文件,例如:

query         = "SELECT * from cities WHERE name='Unknown';"
count         = 0
level         = 1
name          = "Check for cities whose name should be null"
suggested_fix = "UPDATE cities SET name=NULL WHERE name='Unknown';"

这很好用;我使用 Python 的string.partition('=') 划分每一行。

我的问题是很长的 SQL 查询。目前,我只是将这些查询粘贴为单行,这是丑陋且不可维护的。

我想找到一种优雅的 Pythonic 方式来读取表达式的右侧,即使跨越多行。

注意事项:

  • 我的 SQL 查询可能包含 =
  • 我不喜欢在右侧强制使用"s,因为很多现有文件都没有它。

编辑:

ConfigParser 很棒,但它迫使我在多行条目的每行开头添加一个空格或制表符。这可能会很痛苦。

提前致谢,

亚当

【问题讨论】:

  • 这个空格/制表符不包含在最终值中
  • 你能详细说明一下吗?不明白您的评论。

标签: python configuration-files text-parsing sql


【解决方案1】:

Python 标准库模块 ConfigParser 默认支持此功能。配置文件必须是标准格式:

[Long Section]
short: this is a normal line
long: this value continues
    in the next line

上面的配置文件可以用以下代码读取:

import ConfigParser
config = ConfigParser.ConfigParser()
config.read('longsections.cfg')
long = config.get('Long Section', 'long')

【讨论】:

  • 我对此解决方案有疑问,因为在解析文件时会出现错误。根据文档,对于多行值,需要缩进。我在“下一行”的开头添加了 4 个空格,它可以工作。
【解决方案2】:

这几乎正是使我们切换到YAML 的用例(Wikipediapython implementationdocumentation;您可能想查看JSON 作为替代方案)。 YAML 比configparserjson 有一些优势:

  • 人类可读性(对于较大的文件比 JSON 更好);
  • 可以序列化任意python对象(这使得它和pickle一样不安全,但是python实现中有一个safe_load函数可以缓解这个问题)。这对于像 datetime 对象这样简单的东西已经很有用了。

为了完整起见,主要缺点(IMO):

  • Python 实现比 JSON 实现慢一个数量级;
  • 跨平台的可移植性不如 JSON。

例如

import yaml

sql = """
query         : "SELECT * from cities
WHERE name='Unknown';"
count         : 0
level         : 1
name          : "Check for cities whose name should be null"
suggested_fix : "UPDATE cities SET name=NULL WHERE name='Unknown';"
"""

sql_dict = yaml.safe_load(sql)

print(sql_dict['query'])

打印

SELECT * from cities WHERE name='Unknown';

【讨论】:

    【解决方案3】:

    我建议你使用正则表达式...代码可能看起来像这样给你一个开始:

    import re
    
    test="""query = "select * from cities;"
    count = 0
    multine_query = "select *
    from cities
         where name='unknown';"
    """
    
    re_config = re.compile(r'^(\w+)\s*=\s*((?:".[^"]*")|(?:\d+))$', re.M)
    for key, value in re_config.findall(test):
        if value.startswith('"'):
            value = value[1:-1]
        else:
            value = int(value)
        print key, '=', repr(value)
    

    这个例子的输出是:

    ~> python test.py 
    query = 'select * from cities;'
    count = 0
    multine_query = "select *\nfrom cities\n     where name='unknown';"
    

    希望有帮助!

    问候, 克里斯托夫

    【讨论】:

    • +1 应该可以,但我真的更喜欢支持各种边缘条件的现成包。
    猜你喜欢
    • 2016-04-28
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 2018-12-12
    • 2012-09-29
    • 2012-10-25
    • 1970-01-01
    • 2017-11-10
    相关资源
    最近更新 更多