Python 从多个标记的字符串构建pyparsing.Dict-第二部分

Python 从多个标记的字符串构建pyparsing.Dict-第二部分,python,pyparsing,Python,Pyparsing,由于本论坛的反馈,我取得了一些进展(谢谢论坛!)。 正在填充pyparsing.Dict对象Dict,但当它找到十进制数时,会自动失败 鉴于: import pyparsing as pp lines = '''\ (rate multiple) (region "mountainous") (elev 21439) (alteleva +21439) (altelevb -21439) (coorda 23899.747) (coordb +23899.747) (coor

由于本论坛的反馈,我取得了一些进展(谢谢论坛!)。 正在填充pyparsing.Dict对象Dict,但当它找到十进制数时,会自动失败

鉴于:

import pyparsing as pp

lines = '''\
(rate multiple)
(region "mountainous")
(elev       21439)
(alteleva  +21439)
(altelevb  -21439)
(coorda  23899.747)
(coordb +23899.747)
(coordc -23899.747)
(coordd  853.324e21)
(coorde +853.324e21)
(coordf -853.324e21)
(coordg  987.88e+09)
(coordh +987.88e+09)
(coordi -987.88e+09)
(coordj  122.45e-04)
(coordk +122.45e-04)
(coordl -122.45e-04)
'''

leftParen    = pp.Literal('(')
rightParen   = pp.Literal(')')
colon        = pp.Literal(':')
decimalpoint = pp.Literal('.')
doublequote  = pp.Literal('"')
plusorminus  = pp.Literal('+') | pp.Literal('-') 
exp          = pp.CaselessLiteral('E')

v_string = pp.Word(pp.alphanums)
v_quoted_string = pp.Combine( doublequote + v_string + doublequote)
v_number = pp.Regex(r'[+-]?(?P<float1>\d+)(?P<float2>\.\d+)?(?P<float3>[Ee][+-]?\d+)?')

keyy = v_string
valu = v_string | v_quoted_string | v_number

item  = pp.Group( pp.Literal('(').suppress() + keyy + valu + pp.Literal(')').suppress() )
items = pp.ZeroOrMore( item)
dict = pp.Dict( items)

print "dict yields: ",  dict.parseString( lines).dump()
改变令牌的顺序证明了当脚本到达第一个十进制数时,脚本会自动失败,这意味着pp.Regex语句有一些微妙的错误,但我肯定无法发现它

蒂亚


code_warrior

您的问题实际上在于以下表达式:

valu = v_string | v_quoted_string | v_number
v_string = pp.Word(pp.alphanums)
因为
v_string
被定义为非常广泛匹配的表达式:

valu = v_string | v_quoted_string | v_number
v_string = pp.Word(pp.alphanums)
由于它是
valu
中的第一个表达式,因此它将屏蔽以数字开头的
v_数字。这是因为“|”运算符生成
pp.MatchFirst
对象,因此匹配的第一个表达式(从左到右读取)将确定使用哪一个替换项。您可以转换为使用“^”运算符,该运算符生成
pp.Or
对象-
Or
类将尝试评估所有备选方案,然后进行最长匹配。但是,请注意,使用
会带来性能损失,因为即使没有混淆的机会,也会测试更多的表达式以进行匹配。在您的情况下,只需对表达式重新排序,将最不特定的匹配表达式放在最后:

valu = v_quoted_string | v_number | v_string
现在,值将首先被解析为带引号的字符串,然后被解析为数字,并且只有当这些特定类型中的任何一种都不匹配时,才会被解析为非常通用的类型
v_string

其他一些评论:

我个人更喜欢解析带引号的字符串,只获取引号中的内容(这是一个字符串,我已经知道了!)。当解析的字符串显示时没有任何引号,在转储解析结果时,通常会与pyparsing的旧版本产生一些混淆。但是现在我使用repr()来显示解析后的值,当调用
dump()
时,字符串显示在引号中,但值本身不包括引号。当它在程序的其他地方使用时,例如保存到数据库或CSV,我不需要引号,我只需要字符串内容。默认情况下,
QuotedString
类为我处理这个问题。或者使用
pp.quotedString().addParseAction(pp.removeQuotes)

最近的pyparsing版本引入了
pyparsing\u common
名称空间类,其中包含许多有用的预定义表达式。有几种方法可以解析不同的数值类型(整数、有符号整数、实数等),还有一些通用表达式:
number
将解析任何数值类型,并生成相应类型的值(
real
将给出浮点,
integer
将给出int,等等)
fnumber
将解析各种数字,但将它们全部作为浮点数返回。我已经用just
pp.pyparsing_common.number()
替换了您的
v_number
表达式,这也允许我删除仅为构建
v_number
表达式而定义的其他几个部分表达式,如
小数点
加号
exp
。有关
pyparsing\u common
中表达式的更多信息,请访问在线文档:

Pyparsing在处理表达式(如
“(“+pp.Word(pp.alphas)+valu+”)”
中的文字字符串时的默认行为是自动将文字(“and”)术语转换为
pp.literal
对象。这可以防止意外丢失已解析的数据,但在标点符号的情况下,解析结果中会出现许多杂乱无章且毫无帮助的额外字符串。在解析器中,可以通过调用
pp.parseRelation.inlineLiteralUsing
并传递
pp.Suppress
类来替换pyparsing的默认值:

pp.ParserElement.inlineLiteralsUsing(pp.Suppress)
现在您可以编写如下表达式:

item  = pp.Group('(' + keyy + valu + ')')
并且分组括号将从解析结果中被抑制

通过这些更改,您的解析器现在简化为:

import pyparsing as pp

# override pyparsing default to suppress literal strings in expressions
pp.ParserElement.inlineLiteralsUsing(pp.Suppress)

v_string = pp.Word(pp.alphanums)
v_quoted_string = pp.QuotedString('"')
v_number = pp.pyparsing_common.number()

keyy = v_string
# define valu using least specific expressions last
valu = v_quoted_string | v_number | v_string

item  = pp.Group('(' + keyy + valu + ')')
items = pp.ZeroOrMore( item)
dict_expr = pp.Dict( items)

print ("dict yields: ",  dict_expr.parseString( lines).dump())
对于您的测试输入,给出:

dict yields:  [['rate', 'multiple'], ['region', 'mountainous'], ['elev', 21439], 
['alteleva', 21439], ['altelevb', -21439], ['coorda', 23899.747], ['coordb', 
23899.747], ['coordc', -23899.747], ['coordd', 8.53324e+23], ['coorde', 
8.53324e+23], ['coordf', -8.53324e+23], ['coordg', 987880000000.0], ['coordh', 
987880000000.0], ['coordi', -987880000000.0], ['coordj', 0.012245], ['coordk', 
0.012245], ['coordl', -0.012245]]
- alteleva: 21439
- altelevb: -21439
- coorda: 23899.747
- coordb: 23899.747
- coordc: -23899.747
- coordd: 8.53324e+23
- coorde: 8.53324e+23
- coordf: -8.53324e+23
- coordg: 987880000000.0
- coordh: 987880000000.0
- coordi: -987880000000.0
- coordj: 0.012245
- coordk: 0.012245
- coordl: -0.012245
- elev: 21439
- rate: 'multiple'
- region: 'mountainous'

使用pp。或者确实解决了这个问题,一个微妙的问题。非常感谢你花时间写了这么详细的回复,我从中学到了很多。