解析pyparsing组混合字符词

时间:2016-10-03 21:23:38

标签: python pyparsing

我试图使用pyparsing解析来自维基百科信息框的数据字段。首先,以下代码有效:

from pyparsing import *

test_line = """{{Infobox company | name                = Exxon Mobil Corp | num_employees_year  = 2015 }}"""

data_group = Group(
    Suppress("|") +
    OneOrMore(White()).suppress() +
    Word(alphanums + printables)("key") +
    OneOrMore(White()).suppress() +
    Suppress("=") +
    OneOrMore(White()).suppress() +
    OneOrMore(Word(alphanums))("value") +
    ZeroOrMore(White()).suppress()
)

infobox_parser = (
    Literal("{{").suppress() +
    Word("Infobox") +
    White().suppress() +
    Word("company") +
    OneOrMore(White()).suppress() +
    OneOrMore(data_group)("values") +
    Literal("}}").suppress()
)

print(infobox_parser.parseString(test_line))

产生结果:

['Infobox', 'company', ['name', 'Exxon', 'Mobil', 'Corp'], ['num_employees_year', '2015']]

问题是当我将测试字符串更改为

test_line = """{{Infobox company | name                = Exxon Mobil Corp. | num_employees_year  = 2015 }}"""

它失败了,因为我介绍了'。'作为' Corp。'的一部分。我以为我可以通过将Group对象更改为

来解决这个问题
data_group = Group(
    Suppress("|") +
    OneOrMore(White()).suppress() +
    Word(alphanums + printables)("key") +
    OneOrMore(White()).suppress() +
    Suppress("=") +
    OneOrMore(White()).suppress() +
    OneOrMore(Word(alphanums + printables))("value") +
    ZeroOrMore(White()).suppress()
)

但我收到以下错误:

pyparsing.ParseException: Expected "}}" (at char 91), (line:1, col:92)

我在这里缺少什么?提前谢谢。

1 个答案:

答案 0 :(得分:2)

只是一些事情。最重要的是,pyparsing不像正则表达式那样进行相同的回溯。也就是说,这样的事情是行不通的:

data = '{' + OneOrMore(Word(printables))("data") + '}'
print(data.parseString('{ this is some data }'))

为什么呢?由于终止'}'Word(printables)匹配,因此OneOrMore将一直持续到最后,然后失败,因为没有终止'}'到在阅读数据后找到。

直到最近,解决方案是在OneOrMore表达式中包含一个警卫,一个负面的说法实际上说“我想要Word(printables),但首先检查它是否是'}' - 我不喜欢我想要“,看起来像这样:

data = '{' + OneOrMore(~Literal('}') + Word(printables))("data") + '}'

但这很常见,我最近在stopOnZeroOrMore添加了一个可选的OneOrMore参数:

data = '{' + OneOrMore(Word(printables), stopOn=Literal('}'))("data") + '}'

在您的情况下,每个data_group解析一个key=value对,当您只解析OneOrMore(Word(alphanums))时,您的值很好。但是一旦你把它改成OneOrMore(Word(alphanums+printables)),你的重复术语就会贪婪地匹配下一个'|'或者终止'}}',并且就像上面的例子一样失败。

其他几项:

  • pyparsing会为你跳过空格。所有那些White()元素都是完全没必要的。

  • 在某些地方,您使用Word的方式不正确,如Word("Infobox")中所示。在您的有限示例中,这匹配正常,但请记住Word是使用您希望作为单词组匹配的字符集定义的,因此Word("Infobox")不仅会匹配“Infobox”,还会匹配任何其他单词由字母'I','n','f','o','b'和/或'x'组成,例如“Inbox”,“IbIx”,“xoxoxox”等。在此例如,您想要的pyparsing课程为LiteralKeyword

  • 退一步,看起来你的data_groups是key=value对,并且分隔'|'。我建议您使用delimitedList

  • 最后,使用dump()输出已解析的数据,它将有助于可视化结构和结果名称。

通过这些更改,您的代码如下所示:

data_group = Group(
    Word(alphas, alphanums+'_')("key") +
    Suppress("=") +
    originalTextFor(OneOrMore(Word(printables), stopOn=Literal('|') | '}}'))("value") 
)

infobox_parser = (
    Literal("{{").suppress() +
    Keyword("Infobox") +
    Keyword("company") + '|' + 
    Group(delimitedList(data_group, '|'))("values") +
    Literal("}}").suppress()
)

print(infobox_parser.parseString(test_line).dump())

,并提供:

['Infobox', 'company', '|', [['name', 'Exxon Mobil Corp.'], ['num_employees_year', '2015']]]
- values: [['name', 'Exxon Mobil Corp.'], ['num_employees_year', '2015']]
  [0]:
    ['name', 'Exxon Mobil Corp.']
    - key: name
    - value: Exxon Mobil Corp.
  [1]:
    ['num_employees_year', '2015']
    - key: num_employees_year
    - value: 2015