在字符串中的十六进制数前添加'0x'

3
我正在解析一个XML文件,其中包含基本表达式(如id*10+2)。我的目标是对表达式进行评估,以获得实际值。为此,我使用eval()方法,这个方法非常有效。
唯一的问题是数字实际上是十六进制数。如果每个十六进制数都以'0x'为前缀,则eval()方法可以很好地工作,但我找不到一种方法来做到这一点,也没有在这里找到类似的问题。有什么干净的方法可以解决这个问题?

1
你打算如何解析 CAFE+BABE - georg
嗯,CAFE和BABE应该是十六进制数。我需要注意的唯一变量名是“Id”。 因此,理想情况下,它会将“CAFE+BABE”转换为“0xCAFE+0xBABE”。 - Thibault Martin
"open('/tmp/a-file-on-your-system','w').write(\"Careful!\")"这样的表达式怎么样?你试过使用eval吗? - Joe
@Joe谢谢你的警告,但正如我在Fortran的回答中所评论的那样,这只是快速原型制作。稍后会用更安全的东西替换它。 - Thibault Martin
当然。有些事情应该总是指出来(eval,SQL注入),只是为了记录。也许会有一些不知道的人会看到。例如,我看到很多带有SQL注入漏洞的问题,所以我知道说这些话总是值得的。 - Joe
4个回答

4

使用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'

使用负向回溯断言来确保要转换的部分前面没有字母i(防止'id'变成'i0xd')。如果变量是Id,则将i替换为大写的I


我正准备自己按下回车键 ;) - Jon Clements
我尝试了这个解决方案,但它只适用于小于'A'的数字。一旦出现十六进制字符(即从'a'到'f'),它就不会被替换(\d+不允许字母作为数字)。 - Thibault Martin
1
实际上它运行良好...只要Id不是要解析的表达式的一部分!它会被替换为I0xd(我没有预料到这个!) - Thibault Martin
我已经编辑了你的代码,因为变量是Id。非常感谢你快速而有用的回答。 - Thibault Martin

0

一种选择是使用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') (它不会用'0xBABE'替换'BABE')。

另一方面,正则表达式解决方案更简单,也可能涵盖您需要处理的所有情况。


不错的解决方案,我喜欢它!由于我只是在原型设计和处理非常基本的表达式,所以我保持简单而愚蠢,但这个解决方案似乎避免了一些问题。 - Thibault Martin

0
如果您可以将表达式解析为单独的数字,那么我建议使用int function
>>> int("CAFE", 16)
51966

这确实是一个不错的解决方案,但整个问题在于正确拆分表达式(由于十六进制数字中的字母而有点困难)。无论如何还是谢谢! - Thibault Martin

0

小心使用eval!永远不要在不可信的输入中使用它。

如果只是简单的算术运算,我会使用自定义解析器(有很多示例在外面)...而使用解析器生成器(flex/bison、antlr等)是一种有用且容易被遗忘的技能,所以这可能是一个很好的机会来刷新或学习它。


1
谢谢你的建议,我确实阅读了一些关于eval()可以做什么的内容。有点吓人 :D 但是我用它进行快速原型设计(稍后我会为xml文件编写C++读取器/解析器)。而且它只在封闭网络中运行,不连接到互联网!但我同意你的观点,将其保留在最终代码中并不是一个好主意。 - Thibault Martin
是的,用于原型设计很好 :) ... 但要注意,它非常方便,你可能会决定永远使用它!XD(我就遇到这种情况了 :p) - fortran
1
哈哈,我知道这有多冒险了,程序员太懒惰了 :D 但是在我之后,人们会阅读代码的,他们不会让它这样继续下去(而且这是最好的)。 - Thibault Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接