只需在python中使用parsec

Mit*_*ops 6 python parsec parser-combinators

我正在看这个图书馆,那里没有多少文档:https : //pythonhosted.org/parsec/#examples

知道还有其他选择,但我想使用这个库。

我想解析以下字符串:

mystr = """
<kv>
  key1: "string"
  key2: 1.00005
  key3: [1,2,3]
</kv>
<csv>
date,windspeed,direction
20190805,22,NNW
20190805,23,NW
20190805,20,NE
</csv>"""
Run Code Online (Sandbox Code Playgroud)

虽然我想解析整个事情,但我只想抓住<tags>。我有:

>>> import parsec
>>> tag_start = parsec.Parser(lambda x: x == "<")
>>> tag_end = parsec.Parser(lambda x: x == ">")
>>> tag_name = parsec.Parser(parsec.Parser.compose(parsec.many1, parsec.letter))
>>> tag_open = parsec.Parser(parsec.Parser.joint(tag_start, tag_name, tag_end))
Run Code Online (Sandbox Code Playgroud)

好,看起来不错 现在使用它:

>>> tag_open.parse(mystr)
Traceback (most recent call last):
...
TypeError: <lambda>() takes 1 positional argument but 2 were given
Run Code Online (Sandbox Code Playgroud)

这失败了。恐怕我什至不明白我的lambda表达式给出两个参数的含义,这很明显是1.如何进行?

对于所有奖励积分,我的最佳期望输出是:

[
{"type": "tag", 
 "name" : "kv",
 "values"  : [
    {"key1" : "string"},
    {"key2" : 1.00005},
    {"key3" : [1,2,3]}
  ]
},
{"type" : "tag",
"name" : "csv", 
"values" : [
    {"date" : 20190805, "windspeed" : 22, "direction": "NNW"}
    {"date" : 20190805, "windspeed" : 23, "direction": "NW"}
    {"date" : 20190805, "windspeed" : 20, "direction": "NE"}
  ]
}
Run Code Online (Sandbox Code Playgroud)

在这个问题中,我准备理解的输出是使用上述函数来生成start和end标记:

[
  {"tag": "kv"},
  {"tag" : "csv"}
]
Run Code Online (Sandbox Code Playgroud)

并且只需能够从凌乱的混合文本条目中解析出任意类似xml的标签。

sig*_*now 8

我鼓励您使用这些组合器定义自己的解析器,而不是Parser直接构造直接解析器。

如果要Parser通过包装函数来构造A ,如文档所述,fn则应接受两个参数,第一个是文本,第二个是当前位置。并且fn应返回Valueby Value.successValue.failure,而不是布尔值。您可以在此软件包@Parser中的grep 中parsec/__init__.py找到有关其工作方式的更多示例。

对于描述中的情况,可以按如下方式定义解析器:

from parsec import *

spaces = regex(r'\s*', re.MULTILINE)
name = regex(r'[_a-zA-Z][_a-zA-Z0-9]*')

tag_start = spaces >> string('<') >> name << string('>') << spaces
tag_stop = spaces >> string('</') >> name << string('>') << spaces

@generate
def header_kv():
    key = yield spaces >> name << spaces
    yield string(':')
    value = yield spaces >> regex('[^\n]+')
    return {key: value}

@generate
def header():
    tag_name = yield tag_start
    values = yield sepBy(header_kv, string('\n'))
    tag_name_end = yield tag_stop
    assert tag_name == tag_name_end
    return {
        'type': 'tag',
        'name': tag_name,
        'values': values
    }

@generate
def body():
    tag_name = yield tag_start
    values = yield sepBy(sepBy1(regex(r'[^\n<,]+'), string(',')), string('\n'))
    tag_name_end = yield tag_stop
    assert tag_name == tag_name_end
    return {
        'type': 'tag',
        'name': tag_name,
        'values': values
    }

parser = header + body
Run Code Online (Sandbox Code Playgroud)

如果运行parser.parse(mystr),它会产生

({'type': 'tag',
  'name': 'kv',
  'values': [{'key1': '"string"'},
             {'key2': '1.00005'},
             {'key3': '[1,2,3]'}]},
 {'type': 'tag',
  'name': 'csv',
  'values': [['date', 'windspeed', 'direction'],
             ['20190805', '22', 'NNW'],
             ['20190805', '23', 'NW'],
             ['20190805', '20', 'NE']]}
)
Run Code Online (Sandbox Code Playgroud)

您可以values在上面的代码中细化的定义,以获得所需形式的结果。


Ard*_*den 5

根据测试,解析字符串的正确方法如下:

from parsec import *

possible_chars = letter() | space() |  one_of('/.,:"[]') | digit()
parser =  many(many(possible_chars) + string("<") >> mark(many(possible_chars)) << string(">"))

parser.parse(mystr)
# [((1, 1), ['k', 'v'], (1, 3)), ((5, 1), ['/', 'k', 'v'], (5, 4)), ((6, 1), ['c', 's', 'v'], (6, 4)), ((11, 1), ['/', 'c', 's', 'v'], (11, 5))]
Run Code Online (Sandbox Code Playgroud)

的建设parser


为了方便起见,我们首先定义我们希望匹配的字符。parsec提供多种类型:

  • letter(): 匹配任何字母字符,

  • string(str): 匹配任何指定的字符串str

  • space(): 匹配任何空白字符,

  • spaces(): 匹配多个空白字符,

  • digit(): 匹配任何数字,

  • eof(): 匹配字符串的 EOF 标志,

  • regex(pattern): 匹配提供的正则表达式模式,

  • one_of(str): 匹配提供的字符串中的任何字符,

  • none_of(str): 匹配不在提供的字符串中的字符。


根据文档,我们可以用运算符将​​它们分开:

  • |:这个组合器实现选择。解析器 p | q 首先应用 p。如果成功,则返回 p 的值。如果 p 在不消耗任何输入的情况下失败,则尝试解析器 q。注意:没有回溯,

  • +: 将两个或多个解析器合二为一。返回来自这两个解析器的两个结果的聚合。

  • ^: 选择回溯。每当需要任意前瞻时,就会使用此组合器。解析器 p || q首先应用p,如果成功,则返回p的值。如果 p 失败,它会假装它没有消耗任何输入,然后尝试解析器 q。

  • <<: 以指定的解析器结束,最后解析器消耗了结束标志,

  • <: 以指定的解析器结束,最后解析器没有消耗任何输入,

  • >>: 依次组合两个动作,丢弃第一个产生的任何值,

  • mark(p):标记解析器结果的行列信息p


然后有多个“组合器”:

  • times(p, mint, maxt=None):重复解析器pmintmaxt倍,

  • count(p,n): 重复解析器次p n。如果n小于或等于零,则解析器等于返回空列表,

  • (p, default_value=None):使解析器可选。如果成功,返回结果,否则default_value静默返回,不引发任何异常。如果default_value没有提供,None则返回,

  • many(p)p从从不到无限次重复解析器,

  • many1(p):重复解析器p至少一次,

  • separated(p, sep, mint, maxt=None, end=None): ,

  • sepBy(p, sep): 解析零次或多次出现的解析器p,由分隔符分隔sep

  • sepBy1(p, sep): 解析至少出现一次解析器p,由分隔符分隔sep

  • endBy(p, sep):解析零个或多个的p,分离和结束sep

  • endBy1(p, sep):解析在至少一次出现的p,分离和结束sep

  • sepEndBy(p, sep):解析零次或多次出现的p,分隔并可选地以sep

  • sepEndBy1(p, sep): 解析至少一个出现的p,分隔并可选地以 结尾sep


使用所有这些,我们有一个解析器,它匹配 many 的多次出现possible_chars,后跟 a <,然后我们标记possible_charsup的多次出现>