我知道最简单的方法是使用regular expression,但我想知道是否有其他方法可以进行此项检查。
为什么我需要这个?我正在编写一个Python脚本,从SIM卡读取短信(SMS)。在某些情况下,十六进制消息到达,我需要对它们进行一些处理,所以我需要检查收到的消息是否是十六进制的。
当我发送以下短信时:
Hello world!
我的剧本收到了
00480065006C006C006F00200077006F0072006C00640021
但在某些情况下,我收到普通短信(不是十六进制)。所以我需要做一个 if hex 控件。
我正在使用Python 2.6.5。
更新:
这个问题的原因是,(某种程度上)我发送的消息是hex
,而运营商发送的消息(信息消息和广告。)是作为普通字符串接收的。所以我决定进行检查并确保以正确的字符串格式显示消息。
一些额外的细节:我正在使用华为3G调制解调器和PyHumod从SIM卡读取数据。
可能是我的最佳解决方案:
处理此类字符串的最佳方法是使用a2b_hex
(a.k.a。unhexlify
)和utf-16 big endian encoding
(如@JonasWielicki所述):
from binascii import unhexlify # unhexlify is another name of a2b_hex
mystr = "00480065006C006C006F00200077006F0072006C00640021"
unhexlify(mystr).encode("utf-16-be")
>> u'Hello world!'
答案 0 :(得分:60)
(1)使用int()可以很好地完成这项工作,Python会为您完成所有检查:)
int('00480065006C006C006F00200077006F0072006C00640021', 16)
6896377547970387516320582441726837832153446723333914657L
会奏效。如果失败,您将收到ValueError
例外。
简短示例:
int('af', 16)
175
int('ah', 16)
...
ValueError: invalid literal for int() with base 16: 'ah'
(2) 替代将遍历数据并确保所有字符都在0..9
和a-f/A-F
的范围内。 string.hexdigits
('0123456789abcdefABCDEF'
)对此非常有用,因为它包含两个大写和小写数字。
import string
all(c in string.hexdigits for c in s)
将根据字符串True
中数据的有效性返回False
或s
。
简短示例:
s = 'af'
all(c in string.hexdigits for c in s)
True
s = 'ah'
all(c in string.hexdigits for c in s)
False
备注:
正如@ScottGriffiths在下面的评论中正确注明的那样,如果您的字符串在开头包含int()
,则0x
方法将起作用,而逐字符检查将失败。此外,检查字符的集比字符串字符更快,但是短信息字符串很重要,除非你处理很多(很多!)在这种情况下,您可以将stringhexditigs转换为具有set(string.hexdigits)
的集合。
答案 1 :(得分:18)
你可以:
以下是代码:
import string
def is_hex(s):
hex_digits = set(string.hexdigits)
# if s is long, then it is faster to check against a set
return all(c in hex_digits for c in s)
def is_hex(s):
try:
int(s, 16)
return True
except ValueError:
return False
答案 2 :(得分:10)
我知道操作提到regular expressions,但我想为完整性提供这样的解决方案:
def is_hex(s):
return re.fullmatch(r"^[0-9a-fA-F]$", s or "") is not None
<强>性能强>
为了评估这里提出的不同解决方案的性能,我使用了Python的timeit模块。输入字符串是随机生成的,有三种不同的长度,10
,100
,1000
:
s=''.join(random.choice('0123456789abcdef') for _ in range(10))
Levon's解决方案:
# int(s, 16)
10: 0.257451018987922
100: 0.40081690801889636
1000: 1.8926858339982573
# all(_ in string.hexdigits for _ in s)
10: 1.2884491360164247
100: 10.047717947978526
1000: 94.35805322701344
其他答案是这两者的变体。使用正则表达式:
# re.fullmatch(r'^[0-9a-fA-F]$', s or '')
10: 0.725040541990893
100: 0.7184272820013575
1000: 0.7190397029917222
因此,选择正确的解决方案取决于输入字符串的长度以及是否可以安全地处理异常。正则表达式肯定会更快地处理大字符串(并且不会在溢出时抛出ValueError
),但int()
是较短字符串的赢家。
答案 3 :(得分:3)
另一种选择:
def is_hex(s):
hex_digits = set("0123456789abcdef")
for char in s:
if not (char in hex_digits):
return False
return True
答案 4 :(得分:2)
上面提出的大多数解决方案没有考虑任何十进制整数也可以解码为十六进制,因为十进制数字集是十六进制数字集的子集。所以Python很乐意接受123
并假设它是0123
十六进制:
>>> int('123',16)
291
这听起来很明显,但在大多数情况下,你会寻找实际上是十六进制编码的东西,例如:哈希,而不是可以进行十六进制解码的任何内容。因此,可能更强大的解决方案还应该检查十六进制字符串的均匀长度:
In [1]: def is_hex(s):
...: try:
...: int(s, 16)
...: except ValueError:
...: return False
...: return len(s) % 2 == 0
...:
In [2]: is_hex('123')
Out[2]: False
In [3]: is_hex('f123')
Out[3]: True
答案 5 :(得分:2)
这将涵盖如果字符串以&#39; 0x&#39;开头的情况。或者&#39; 0X&#39;:[0x | 0X] [0-9a-fA-F]
d='0X12a'
all(c in 'xX' + string.hexdigits for c in d)
True
答案 6 :(得分:2)
基于字符串转换以设置和检查子集(不检查'0x'前缀)的另一种简单快捷的解决方案:
import string
def is_hex_str(s):
return set(s).issubset(string.hexdigits)
更多信息here。
答案 7 :(得分:0)
使用Python你要确定是真还是假,我会使用eumero的is_hex方法而不是Levon的方法之一。以下代码包含一个问题......
if int(input_string, 16):
print 'it is hex'
else:
print 'it is not hex'
错误地将字符串'00'报告为而不是十六进制,因为零的计算结果为False。
答案 8 :(得分:0)
在Python3中,我尝试过:
def is_hex(s):
try:
tmp=bytes.fromhex(hex_data).decode('utf-8')
return ''.join([i for i in tmp if i.isprintable()])
except ValueError:
return ''
它应该比方式更好:int(x,16)
答案 9 :(得分:0)
由于上面的所有正则表达式都花费了相同的时间,因此我想大多数时间与将字符串转换为正则表达式有关。以下是我预编译正则表达式时获得的数据。
int_hex
0.000800 ms 10
0.001300 ms 100
0.008200 ms 1000
all_hex
0.003500 ms 10
0.015200 ms 100
0.112000 ms 1000
fullmatch_hex
0.001800 ms 10
0.001200 ms 100
0.005500 ms 1000
答案 10 :(得分:0)
一种简单的解决方案,以防您需要一种模式来验证带前缀的十六进制或二进制以及十进制
\b(0x[\da-fA-F]+|[\d]+|0b[01]+)\b
示例:https://regex101.com/r/cN4yW7/14
然后在python中执行int('0x00480065006C006C006F00200077006F0072006C00640021', 0)
会得到
6896377547970387516320582441726837832153446723333914657
基数0调用前缀猜测行为。 这为我节省了很多麻烦。希望对您有帮助!