由于本论坛的反馈,我已经取得了一些进展(感谢论坛!)。 pyparsing.Dict对象dicct正在填充,但在找到十进制数时会无声地失败。
下式给出:
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)
dicct = pp.Dict( items)
print "dicct yields: ", dicct.parseString( lines).dump()
产量
- alteleva: '+21439',
- altelevb: '-21439',
- elev: '21439',
- rate: 'multiple',
- region: '"mountainous"'
改变令牌的顺序证明当脚本遇到第一个十进制数时,脚本会无声地失败,这意味着pp.Regex语句出现了一些巧妙的错误,但我确定无法发现它。 / p>
TIA,
code_warrior
答案 0 :(得分:1)
你的问题实际上在于这个表达式:
valu = v_string | v_quoted_string | v_number
因为v_string
被定义为非常广泛匹配的表达式:
v_string = pp.Word(pp.alphanums)
因为它是valu
中的第一个表达式,它将屏蔽以数字开头的v_numbers
。这是因为'|'运算符生成pp.MatchFirst
个对象,因此匹配的第一个表达式(从左到右读取)将确定使用哪个替代项。您可以转换为使用生成pp.Or
个对象的'^'运算符 - Or
类将尝试评估所有备选方案,然后使用最长匹配。但请注意,使用Or
会带来性能损失,因为即使没有混淆的机会,也会有更多表达式测试匹配。在您的情况下,您可以重新排序表达式以将最不具体的匹配表达式放在最后:
valu = v_quoted_string | v_number | v_string
现在,首先尝试解析值作为带引号的字符串,然后作为数字进行解析,然后仅当这些特定类型与这两种特定类型都不匹配时才会被解析,作为非常通用的类型v_string
。
其他一些评论:
我个人更喜欢解析引用的字符串,只获得引号内的内容(这是一个字符串,我已经知道了!)。在显示解析后的字符串而没有任何封闭引号的情况下,在转出解析结果时,曾经与旧版本的pyparsing存在一些混淆。但是现在我使用repr()来显示已解析的值,在调用dump()
时字符串会显示在引号中,但值本身不包含引号。当它在程序的其他地方使用时,比如保存到数据库或CSV,我不需要引号,我只想要字符串内容。默认情况下,QuotedString
类会为我处理此问题。或者使用pp.quotedString().addParseAction(pp.removeQuotes)
。
最近的一个pyparsing版本引入了pyparsing_common
命名空间类,其中包含许多有用的预定义表达式。有几种用于解析不同的数字类型(整数,有符号整数,实数等),以及几个一揽子表达式:number
将解析任何数字类型,并生成相应类型的值({{1}将给出一个浮点数,real
将给出一个int等等); integer
将解析各种数字,但将它们全部作为浮点数返回。我已将fnumber
表达式替换为v_number
,这也允许我删除为构建pp.pyparsing_common.number()
表达式而定义的其他几个部分表达式,例如v_number
,decimalpoint
和plusorminus
。您可以在在线文档exp
pyparsing_common
中表达式的更多信息
在"(" + pp.Word(pp.alphas) + valu + ")"
这样的表达式中处理文字字符串时,Pyparsing的默认行为是自动将文字“(”和“)”术语转换为pp.Literal
个对象。这可以防止意外丢失已解析的数据,但在标点符号的情况下,您最终会在解析的结果中出现许多混乱且无用的额外字符串。在您的解析器中,您可以通过调用pp.ParserElement.inlineLiteralsUsing
并传递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)
dicct = pp.Dict( items)
print ("dict yields: ", dicct.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'