我正在解析一个 xml 文件,在其中我得到基本表达式(如id*10+2
)。我要做的是评估表达式以实际获取值。为此,我使用eval()
效果很好的方法。
唯一的问题是这些数字实际上是十六进制数字。如果每个十六进制数字都以“0x”为前缀,则该eval()
方法可以很好地工作,但是我找不到方法,在这里也找不到类似的问题。如何以干净的方式完成?
使用re
模块。
>>> import re
>>> re.sub(r'([\dA-F]+)', r'0x\1', 'id*A+2')
'id*0xA+0x2'
>>> eval(re.sub(r'([\dA-F]+)', r'0x\1', 'CAFE+BABE'))
99772
但是请注意,如果输入无效eval
,它将无法正常工作。使用也有很多风险eval
。
如果您的十六进制数字有小写字母,那么您可以使用:
>>> re.sub(r'(?<!i)([\da-fA-F]+)', r'0x\1', 'id*a+b')
'id*0xa+0xb'
这使用否定的lookbehind断言来确保字母i
不在它尝试转换的部分之前(防止'id'
变成.如果变量是. 则'i0xd'
替换i
为.I
Id
如果您可以将表达式解析为单个数字,那么我建议使用int 函数:
>>> int("CAFE", 16)
51966
小心eval
!永远不要在不受信任的输入中使用它。
如果它只是简单的算术,我会使用自定义解析器(野外有大量示例)......并且使用解析器生成器(flex / bison,antlr等)是一种有用且容易忘记的技能,所以这可能是一个刷新或学习它的好机会。
一种选择是使用该parser
模块:
import parser, token, re
def hexify(ast):
if not isinstance(ast, list):
return ast
if ast[0] in (token.NAME, token.NUMBER) and re.match('[0-9a-fA-F]+$', ast[1]):
return [token.NUMBER, '0x' + ast[1]]
return map(hexify, ast)
def hexified_eval(expr, *args):
ast = parser.sequence2st(hexify(parser.expr(expr).tolist()))
return eval(ast.compile(), *args)
>>> hexified_eval('id*10 + BABE', {'id':0xcafe})
567466
这比正则表达式解决方案要干净一些,因为它只尝试替换已确定为名称或数字(并且看起来像十六进制数字)的标记。它还可以正确处理更通用的 python 表达式,例如id*10 + len('BABE')
(它不会替换'BABE'
为'0xBABE'
)。
OTOH,正则表达式解决方案更简单,并且可能涵盖您需要处理的所有情况。