下式给出:
innput = '''
(crossSectionDescriptor
(layer 0
(layerType SURFACE)
(layerMaterial "AIR")
(layerThickness 0)
(layerElectricalConductivity 0)
(layerThermalConductivity 0.00027)
(layerDielectricConstant 1.000000)
)
(layer 1
(layerType CONDUCTOR)
(layerName "TOP")
(layerMaterial "COPPER")
(layerThickness 1.200000)
(layerElectricalConductivity 595900.000000)
(layerThermalConductivity 3.980000)
(layerDielectricConstant 1.000000)
(layerArtworkNegativeFlag FALSE)
(layerIsShieldFlag FALSE)
)
(layer 2
(layerType DIELECTRIC)
(layerMaterial "FR-4")
(layerThickness 8.000000)
(layerElectricalConductivity 0)
(layerThermalConductivity 0.012)
(layerDielectricConstant 4.500000)
(layerLossTangent 0.035)
)
'''
innput_forced = '''
(crossSectionDescriptor
(layer
(0
(layerType SURFACE)
(layerMaterial "AIR")
(layerThickness 0)
(layerElectricalConductivity 0)
(layerThermalConductivity 0.00027)
(layerDielectricConstant 1.000000)
)
(1
(layerType CONDUCTOR)
(layerName "TOP")
(layerMaterial "COPPER")
(layerThickness 1.200000)
(layerElectricalConductivity 595900.000000)
(layerThermalConductivity 3.980000)
(layerDielectricConstant 1.000000)
(layerArtworkNegativeFlag FALSE)
(layerIsShieldFlag FALSE)
)
(2
(layerType DIELECTRIC)
(layerMaterial "FR-4")
(layerThickness 8.000000)
(layerElectricalConductivity 0)
(layerThermalConductivity 0.012)
(layerDielectricConstant 4.500000)
(layerLossTangent 0.035)
)
)
)
'''
def adjustmentNeeded( toks): # s, loc, toks are implicit
adjustmentNeeded = True
nodeNameSet = set()
for tok in toks:
if type( tok) is pp.ParseResults \
and len( tok) > 2 \
and type( tok[0]) is not pp.ParseResults \
and type( tok[1]) is not pp.ParseResults \
and restAreLists( tok[2:]):
nodeNameSet.add( tok[0]) # silently rejects duplicates
else:
adjustmentNeeded = False
# break
if adjustmentNeeded:
# all tokens are lists of length 3 or more where the first element is the same value and the second element is not a list
new_toks = [ nodeNameSet.pop()]
for tok in toks:
tok.pop(0) # removes redundant layer
new_toks.append( tok.asList() )
return( pp.ParseResults( new_toks ) )
else:
return( toks)
v_string = pp.Word(pp.alphanums+'_'+'-'+'.')
v_quoted_string = pp.Combine( '"' + v_string + '"')
v_number = pp.Regex(r'[+-]?(?P<float1>\d+)(?P<float2>\.\d+)?(?P<float3>[Ee][+-]?\d+)?'
nodeName = v_string
keyy = v_string
valu = pp.Or( [ v_string, v_quoted_string, v_number])
item = pp.Group( pp.Literal('(').suppress() + keyy + pp.OneOrMore( valu) + pp.Literal(')').suppress() )
node = pp.Forward() # recursive structure
node << pp.Dict( pp.Group( pp.Literal('(').suppress() + \
nodeName + \
pp.Optional( valu)('valu') + \
pp.Dict( pp.OneOrMore( item ^ dict_node)).setParseAction( adjustmentNeeded) + \
pp.Literal(')').suppress()
)
) #.setParseAction( makeAdjustments)
pprinter = pprint.PrettyPrinter( indent=1)
现在使用这个:
pprinter.pprint(node.parseString(innput).asDict())
产量
{'crossSectionDescriptor': ['layer',
['0',
['layerType', 'SURFACE'],
['layerMaterial', '"AIR"'],
['layerThickness', '0'],
['layerElectricalConductivity', '0'],
['layerThermalConductivity', '0.00027'],
['layerDielectricConstant', '1.000000']],
['1',
['layerType', 'CONDUCTOR'],
['layerName', '"TOP"'],
['layerMaterial', '"COPPER"'],
['layerThickness', '1.200000'],
['layerElectricalConductivity',
'595900.000000'],
['layerThermalConductivity', '3.980000'],
['layerDielectricConstant', '1.000000'],
['layerArtworkNegativeFlag', 'FALSE'],
['layerIsShieldFlag', 'FALSE']],
['2',
['layerType', 'DIELECTRIC'],
['layerMaterial', '"FR-4"'],
['layerThickness', '8.000000'],
['layerElectricalConductivity', '0'],
['layerThermalConductivity', '0.012'],
['layerDielectricConstant', '4.500000'],
['layerLossTangent', '0.035']]]}
令人心碎地接近我想要的
这里的目标是创建一系列嵌套的dicts。我添加了一个setParseAction函数来按摩节点&#39;子节点变成适当的词组。
这只是一个更大文件的一小部分 - 我不能假设这个&#39;层&#39;将始终是恒定的第一元素,因此无法对其进行编码。事实上,如果有一种方法可以使用pp.addCondition()来表示&#34;一个或多个dict_nodes,其中每个节点具有相同的第一个标记&#34;,这可能是有用的。
任何建议,指示或建设性批评都是值得赞赏的。
TIA,
code_warrior
答案 0 :(得分:0)
定义node_name
,以便识别您的“图层#”表单,并只返回图层编号:
layer_num = pp.Suppress('layer') + pp.Word(pp.nums)
nodeName = layer_num | v_string
使用'|'运算符为您提供MatchFirst
,您强制pyparsing首先尝试layer_num
表达式,如果v_string
表达式失败,则只接受layer_num
。
您还可以通过定义layer_num
来保留索引的“图层”部分,以返回字符串'layer _#':
layer_num = ('layer' + pp.Word(pp.nums)).setParseAction('_'.join)
这将为您提供'layer_0','layer_1'等
的图层索引其他一些提示,可能适用也可能不适用,我不完全了解您的数据:
试试v_string
:
# v_string = pp.Word(pp.alphanums+'_'+'-'+'.')
v_string = pp.Word(pp.alphas, pp.alphanums + '_-.')
只用一个参数定义v_string
的方式,你会接受像'.....','。 - .-。--- ....-。-__'这样的字符串,'234234.02340'。 2参数形式强制v_strings以字母字符开头,这更有可能防止其他不需要的形式(仍然会接受类似'abc .....'的东西,但这也可以解决)。
Pyparsing有几个提供的字符串表达式:
这些表达式也包括封闭引号,与QuotedString
类不同。如果要删除它们,请添加pyparsing parse操作removeQuotes
。
# v_quoted_string = pp.Combine( '"' + v_string + '"')
v_quoted_string = pp.dblQuotedString
新的pyparsing_common
命名空间类包含几种匹配常用表达式的格式,包括整数,实数,IP地址等。数值表达式是使用转换解析操作定义的,因此解析后的值已经转换为Python整数和浮点数。 pyparsing_common
还包括两个任意数字表达式:
您的v_number
可以替换为:
# v_number = pp.Regex(r'[+-]?(?P<float1>\d+)(?P<float2>\.\d+)?(?P<float3>[Ee][+-]?\d+)?')
v_number = pp.pyparsing_common.fnumber()
还有一个额外好处,即数值将在分析时转换为实际的浮点数。
沿着类似的行,你可以定义一个布尔类型来匹配TRUE
和FALSE
等值:
v_boolean = pp.Keyword('TRUE') | pp.Keyword('FALSE')
v_boolean.addParseAction(lambda t: t[0] == 'TRUE')
解析操作将'TRUE'转换为Python True,将'FALSE'转换为Python False。然后将其拼接到valu
:
valu = v_quoted_string | v_number | v_boolean | v_string
最后,我一直偏爱MatchFirst而不是,因为我认为大多数情况下有替代方案,如果按正确的顺序进行测试,它们可以毫不含糊地解决。这是我的valu
版本:
# valu = pp.Or( [ v_string, v_quoted_string, v_number])
valu = v_quoted_string | v_number | v_string
valu = pp.MatchFirst([v_quoted_string, v_number, v_string]) # if you prefer
由于v_number
和v_string
不接受引号,因此在预定数字或不带引号的字符串时,首先检查带引号的字符串是不会意外匹配的。类似地,在检查字符串之前检查数字表单将选择更具体的表达式,而不是全部匹配 - 任意组合的字母 - 数字 - 点 - 破折号或下划线。相反,Or
将始终测试每个给定的表达式,并选择最长的匹配。我发现这很浪费 - 如果你的valu
遇到一个带引号的字符串,它仍然会尝试将其解析为数字和不带引号的字符串,即使这些匹配是不可能的。
另外,我个人的口味是使用pyparsing定义的运算符:'|'适用于MatchFirst
,'{'代表Or
,'+'代表And
,'&amp;'对于Each
,{〜}代表NotAny
。对我来说,它使语法更类似于BNF,并且比带有表达式列表的显式类构造调用更具可读性。但这纯粹是我自己的风格 - 你编写代码的方式没有任何问题。