您发布的代码中有一些小错别字(firstame="bob"
在 data vs. firstname="bob"
, lastname
vs.中last
),但是在清理它们之后,它看起来还不错。如果你打印出结果,你会得到:
['firstname', 'bob', 'lastname', 'stewart', 'dob', '2010-0206',
'hobbies', 'reading, singing', 'drawing', 'is_minor', 'True']
首先,让我建议,正如您将list_of_names
(从您之前的 SO 问题 pyparsing string of quoted names)定义为可能的值类型一样,您定义一个布尔值来解析 True/False 值。使用oneOf
很好,让我们添加一个解析操作,将字符串“True”和“False”转换为实际的 Python 布尔值:
boolean_value = oneOf("True False").setParseAction(lambda t: t[0]=='True')
这类似于removeQuotes
在quotedString 上使用。
现在,解析结果现在看起来像:
['firstname', 'bob', 'lastname', 'stewart', 'dob', '2010-0206',
'hobbies', 'reading, singing', 'drawing', 'is_minor', True]
请注意,True 现在不是字符串,而是 Python 值True
(值周围没有引号)。
现在到你问题的第一部分,如何把它变成一个字典。Pyparsing 允许您为语法的不同部分定义结果名称,以便在解析数据后,您可以按名称访问这些值。这样做的语法过去是调用方法setResultsName
:
my_data = person_start + first.setResultsName("firstname") +
last.setResultsName("lastname") + ...
我发现这有点麻烦,并且所有“.setResultsName”方法调用都难以阅读表达式。所以不久前我更改了 API 以接受这种语法:
my_data = person_start + first("firstname") + last("lastname") + ...
但是您定义为first
,last
等的内容不仅包含值,还包含标签。
简化语法的一种方法是创建一个自己的小辅助方法,我们称之为named_parameter
:
def named_parameter(label, paramtype):
expr = Literal(label) + Suppress('=') + paramtype(label)
return expr
请注意,label
它用于指定文字字符串和值的结果名称。现在您可以将语法定义为:
first = named_parameter("firstname", quotedString)
last = named_parameter("lastname", quotedString)
dob = named_parameter("dob", quotedString)
hobbies = named_parameter("hobbies", Suppress("[") + list_of_names + Suppress("]"))
is_minor = named_parameter("is_minor", boolean_value)
使用命名的值,您可以将解析结果作为 Python dict 访问:
print result["firstname"]
print result["hobbies"]
印刷:
bob
['reading, singing', 'drawing']
或者,如果您愿意,也可以使用对象属性表示法:
print result.firstname
print result.hobbies
为了回答您问题的第二部分,您询问了如何处理参数可能乱序的情况。最简单的方法是delimitedList
再次使用:
parameter = first | last | dob | hobbies | is_minor
my_data = person_start + delimitedList(parameter) + person_end
这不是一个严格的解析器,它会接受不包含所有参数的参数列表,或者具有重复参数的列表。但是对于现有的有效代码,它将以任何顺序解析带有参数的列表。
这是最终的解析器:
quotedString.setParseAction(removeQuotes)
list_of_names = delimitedList(quotedString)
boolean_value = oneOf("True False").setParseAction(lambda t: t[0]=='True')
def named_parameter(label, paramtype):
expr = Literal(label) + Suppress('=') + paramtype(label)
return expr
person_start = Literal("person(").suppress()
first = named_parameter("firstname", quotedString)
last = named_parameter("lastname", quotedString)
dob = named_parameter("dob", quotedString)
hobbies = named_parameter("hobbies", Suppress("[") + list_of_names + Suppress("]"))
is_minor = named_parameter("is_minor", boolean_value)
person_end = Suppress(")")
comma = Literal(",").suppress()
parameter = first | last | dob | hobbies | is_minor
my_data = person_start + delimitedList(parameter) + person_end