在python中简单地使用parsec

2024-06-01 00:51:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我在看这个库,里面几乎没有文档: https://pythonhosted.org/parsec/#examples

understand there are alternatives,但我想用这个库。在

我要解析以下字符串:

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>"""

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

^{pr2}$

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

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

这失败了。恐怕我甚至不明白我的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"}
  ]
}

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

[
  {"tag": "kv"},
  {"tag" : "csv"}
]

并且能够简单地从混乱的混合文本条目中解析任意的类似xml的标记。在


Tags: csvlambdadatestringtagnekey2key1
3条回答

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

如果您想通过包装函数来构造Parser,如文档所述,fn应该接受两个参数,第一个是文本,第二个是当前位置。并且fn应该返回Value,由Value.success或{}返回,而不是布尔值。您可以在这个包中的parsec/__init__.py中grep@Parser,以找到关于它如何工作的更多示例。在

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

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

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

^{pr2}$

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

由于解析器需要一个具有两个可选结果(和两个参数)的函数,您可以考虑中断函数参数,而不是尝试使用内联函数定义(lambda

A Parser is an object that wraps a function to do the parsing work. Arguments of the function should be a string to be parsed and the index on which to begin parsing. The function should return either Value.success(next_index, value) if parsing successfully, or Value.failure(index, expected) on the failure

但是,如果您想使用lambda表达式,那么可以使用lambda这样的参数来指定两个必需的参数:(不确定Value.success或{}在不阅读文档的情况下如何工作。)

lamdba x,y: Value.Success(y+1, x) if x[y] == "<" else Value.failure(y, x)

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

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))]

parser的构造:


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

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

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

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

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

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

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

  • regex(pattern):匹配提供的regex模式,

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

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


根据文件,我们可以用操作员将它们分开:

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

  • +:将两个或多个解析器合并为一个。返回两个结果的总和 从这两个解析器。

  • ^:带回溯的选择。这个组合词在任何时候都可以任意使用 需要向前看。解析器p | | q首先应用p,如果成功, 返回p的值。如果p失败,它假装它没有消耗 任何输入,然后尝试解析器q。

  • <<:以指定的解析器结束,在结尾解析器使用 结束标志,

  • <:以指定的解析器结尾,而在结尾解析器尚未使用 任何输入,

  • >>:按顺序组合两个操作,丢弃产生的任何值 第一个,

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


还有多个“组合体”:

  • times(p, mint, maxt=None):将解析器pmint重复到maxt次,

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

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

  • many(p):重复解析器p从never到无穷多次,

  • 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结尾。


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

相关问题 更多 >