模块在python中解析一个简单的语法

时间:2015-03-13 10:40:28

标签: python dictionary

我的目标是使用Python 2.7.6读取Linux下的文件/etc/os-release并获取字典。 该文件在Ubuntu中看起来像这样:

NAME="Ubuntu"
VERSION="14.04.2 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.2 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

我希望最终得到这样的Python代码会创建的字典结果:

{
'NAME': 'Ubuntu',
'VERSION': '14.04.2 LTS, Trusty Tahr',
'ID': 'ubuntu',
'ID_LIKE': 'debian',
'PRETTY_NAME': 'Ubuntu 14.04.2 LTS',
'VERSION_ID': '14.04',
'HOME_URL': 'http://www.ubuntu.com/',
'SUPPORT_URL': 'http://help.ubuntu.com/',
'BUG_REPORT_URL': 'http://bugs.launchpad.net/ubuntu/',
}

我在Python库中找到了一些解析器,但每个解析器都解析了其他更复杂的语法。我在这里找到了一些示例,但是所有这些都是针对更复杂的语法,并回答了如何编写解析器我只是在寻找已经存在的模块/功能。如果没有,那么,我将只编写自己的代码(所以我不寻找代码示例,除非它是我应该做的)。麻烦的是,我甚至不知道这种语法的名称。虽然我目前的项目是将/etc/os-release作为字典,但我希望将来需要对类似语法的其他数据执行此操作,因此我的搜索重点是语法,而不是文件。

我认为这个简单的东西应该已经存在。

有趣的是,SO的代码示例显示突出显示,就像这里的Web代码可以解析它而不是Python。

4 个答案:

答案 0 :(得分:1)

通过拆分来制作自己的词典:

with open("/etc/os-release") as f:
    d = {}
    for line in f:
        k,v = line.rstrip().split("=")
        d[k] = v
print(d)

如果您确实要删除引号,可以使用strip:

with open("/etc/os-release") as f:
    d = {}
    for line in f:
        k,v = line.rstrip().split("=")
        # .strip('"') will remove if there or else do nothing
        d[k] = v.strip('"') 
print(d)


{'VERSION': '14.04.2 LTS, Trusty Tahr', 'NAME': 'Ubuntu', 'HOME_URL': 'http://www.ubuntu.com/', 'ID': 'ubuntu', 'VERSION_ID': '14.04', 'SUPPORT_URL': 'http://help.ubuntu.com/', 'PRETTY_NAME': 'Ubuntu 14.04.2 LTS', 'BUG_REPORT_URL': 'http://bugs.launchpad.net/ubuntu/', 'ID_LIKE': 'debian'}
{'BUG_REPORT_URL': 'http://bugs.launchpad.net/ubuntu/',
 'HOME_URL': 'http://www.ubuntu.com/',
 'ID': 'ubuntu',
 'ID_LIKE': 'debian',
 'NAME': 'Ubuntu',
 'PRETTY_NAME': 'Ubuntu 14.04.2 LTS',
 'SUPPORT_URL': 'http://help.ubuntu.com/',
 'VERSION': '14.04.2 LTS, Trusty Tahr',
 'VERSION_ID': '14.04'}

答案 1 :(得分:1)

Padraic Cunningham的答案是完美的,我只是跟进回答第二部分(解析那些可以或不可能的引号):如果你想要删除所有引号,只需在Padraic的代码中添加两行。

with open("/etc/os-release") as f:
    d = {}
    for line in f:
        k,v = line.rstrip().split("=")
        if v.startswith('"'):
          v = v[1:-1]
        d[k] = v
print(d)

这是因为我们可以假设如果属性以双引号开头,它将以相同的方式结束。如果你想对单引号也有相同的行为,只需在if中的OR中放入第二个条件。

答案 2 :(得分:1)

根据man page of os-release

  

如果变量赋值必须包含空格,分号或A-Z,a-z,0-9之外的其他特殊字符,则必须用双引号或单引号括起来。 Shell特殊字符(“$”,引号,反斜杠,反引号)必须使用反斜杠进行转义,遵循shell样式。以“#”开头的行应作为注释忽略。

在为文件编写解析器时,必须考虑上面引用中指定的每个规则。它很复杂,因为它被设计为作为shell脚本执行。实现这意味着实现shell的一部分。

对于引号,除了包含复杂的字符串之外没有任何意义,因此可以忽略。您必须考虑的另一件事是,由'='分割不是100%正确,因为在一行中可能有多个'='。由第一个'='的位置拆分。

答案 3 :(得分:0)

它也可以使用现代(3.5+)python作为简单的dict理解来实现:

  has_many :shop_orders
  has_many :order_details, through: :shop_orders

这会生成一个这样的字典

{
    k.lower(): v.strip('\'"')
    for k, v in (
        line.strip().split('=', 1)
        for line in open('/etc/os-release').read().strip().split('\n')
    )
}

或者,我发现一个namedtuple非常有用:

{'name': 'Ubuntu',
 'version': '18.04.2 LTS (Bionic Beaver)',
 'id': 'ubuntu',
 'id_like': 'debian',
 'pretty_name': 'Ubuntu 18.04.2 LTS',
 'version_id': '18.04',
 'home_url': 'https://www.ubuntu.com/',
 'support_url': 'https://help.ubuntu.com/',
 'bug_report_url': 'https://bugs.launchpad.net/ubuntu/',
 'privacy_policy_url': 'https://www.ubuntu.com/legal/terms-and-policies/privacy-policy',
 'version_codename': 'bionic',
 'ubuntu_codename': 'bionic'}

这会生成一个具有所有可作为属性访问的变量的namedtuple:

from collections import namedtuple
with open('/etc/os-release') as f:
    keys, values = zip(*[
        (k.lower(), v.strip('\'"'))
        for (k, v) in (
            line.strip().split('=', 1) for line in f.read().strip().split('\n')
        )]
    ) 
r = namedtuple("OSRelease", keys)(*values)