PyParsing表示函数的字符串

时间:2013-11-10 00:56:32

标签: python pyparsing

我有一个看起来像这样的数据:

data = 'person(firstame="bob", lastname="stewart", dob="2010-0206", hobbies=["reading, singing", "drawing"], is_minor=True)'

我写了语法解析规则如下:

quotedString.setParseAction(removeQuotes)
list_of_names = delimitedList(quotedString)

person_start = Literal("person(").suppress()
first = Literal("firstname") + Suppress("=") + quotedString
lastname = Literal("lastname") + Suppress("=") + quotedString
dob = Literal("dob") + Suppress("=") + quotedString
hobbies = Literal("hobbies") + Suppress("=[") + list_of_names + Suppress("]")
is_minor = Literal("is_minor") + Suppress("=") + oneOf("True False")
person_end = Suppress(")")
comma = Literal(",").suppress()

my_data = person_start + first +  comma + last + comma + dob +comma + hobbies + comma + is_minor + person_end
result = my_data.parseString(data)

我的问题是3:

  1. 上述规则有效但我想确定是否有更好的方法来写这个。
  2. 在我的数据中,订单无法保证,因此姓氏可以在firstname之前,我该如何确保。
  3. 最终解析之后我想把所有内容都作为一个dict,所以关键:值 第一:“鲍勃” 爱好:[“阅读”,“唱歌”,“绘画”] ...... 什么是最好的方法。

2 个答案:

答案 0 :(得分:1)

你应该真的分解它,所以它不依赖于文字......所以寻找令牌“X = Y”以使它更通用......

或者,另一个选项(因为看起来你正试图解析一个Python函数调用),就像是这样的:

data = 'person(firstame="bob", lastname="stewart", dob="2010-0206", hobbies=["reading, singing", "drawing"], is_minor=True)'

import ast
d = {}
for kw in ast.parse(data).body[0].value.keywords:
    if isinstance(kw.value, ast.List):
        d[kw.arg] = [el.s for el in kw.value.elts]
    else:
        d[kw.arg] = getattr(kw.value, {ast.Name: 'id', ast.Str: 's'}[type(kw.value)])

# {'dob': '2010-0206', 'lastname': 'stewart', 'is_minor': 'True', 'firstame': 'bob', 'hobbies': ['reading, singing', 'drawing']} 

答案 1 :(得分:1)

您发布的代码中有一些小错字(firstame="bob"数据与firstname="bob"lastnamelast),但清理后,它看起来很不错。如果您打印出结果,则会得到:

['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')

这类似于在quotedString上使用removeQuotes

现在,解析后的结果如下:

['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") + ...

但您定义为firstlast等的内容不仅包含值,还包含标签。

简化语法的一种方法是制作一个自己的小辅助方法,让我们称之为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