59

我知道最简单的方法是使用正则表达式,但我想知道是否还有其他方法可以进行此检查。

为什么我需要这个?我正在编写一个从SIM卡读取短信 (SMS) 的 Python 脚本。在某些情况下,十六进制消息到达,我需要为它们做一些处理,所以我需要检查收到的消息是否是十六进制的。

当我发送以下短信时:

Hello world!

我的脚本收到

00480065006C006C006F00200077006F0072006C00640021

但在某些情况下,我会收到正常的短信(不是十六进制的)。所以我需要做一个if 十六进制控制。

我正在使用 Python 2.6.5。

更新:

该问题的原因是,(不知何故)我发送的消息被接收,hex而运营商发送的消息(信息消息和广告。)被作为普通字符串接收。所以我决定进行检查并确保我收到的消息是正确的字符串格式。

一些额外的细节:我正在使用华为 3G 调制解调器和PyHumod从 SIM 卡读取数据。

我的情况可能的最佳解决方案:

处理此类字符串的最佳方法是使用a2b_hex(aka 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!'
4

13 回答 13

101

(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..9a-f/A-Fstring.hexdigits( '0123456789abcdefABCDEF') 对此很有用,因为它包含大写和小写数字。

import string
all(c in string.hexdigits for c in s)

将根据您在 string 中数据的有效性返回True或返回。Falses

简短的例子:

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在开头包含,则该方法将起作用,而逐个字符的检查将因此失败。此外,检查一字符比一串字符要快,但是这对于短 SMS 字符串是否很重要,除非您按顺序处理许多(很多!)它们,在这种情况下,您可以将 stringhexditigs 转换为一组与set(string.hexdigits).

于 2012-07-21T12:41:50.783 回答
28

你可以:

  1. 测试字符串是否只包含十六进制数字(0…9,A…F)
  2. 尝试将字符串转换为整数并查看它是否失败。

这是代码:

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
于 2012-07-21T12:43:41.373 回答
21

我知道操作中提到的正则表达式,但为了完整起见,我想贡献这样一个解决方案:

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的解决方案:

# 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()它是较短字符串的赢家。

于 2015-12-14T06:35:04.483 回答
5

一种基于字符串转换以设置和检查子集的更简单和简短的解决方案(不检查'0x'前缀):

import string
def is_hex_str(s):
    return set(s).issubset(string.hexdigits)

更多信息在这里

于 2018-12-27T10:20:27.983 回答
3

另外的选择:

def is_hex(s):
    hex_digits = set("0123456789abcdef")
    for char in s:
        if not (char in hex_digits):
            return False
    return True
于 2013-01-24T18:48:35.603 回答
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
于 2017-04-29T19:42:24.600 回答
1

这将涵盖字符串以“0x”或“0X”开头的情况:[0x|0X][0-9a-fA-F]

d='0X12a'
all(c in 'xX' + string.hexdigits for c in d)
True
于 2018-04-16T21:10:22.093 回答
0

使用 Python 来确定 True 或 False,我会使用 eumero 的 is_hex 方法而不是 Levon 的方法一。以下代码包含一个陷阱...

if int(input_string, 16):
    print 'it is hex'
else:
    print 'it is not hex'

它错误地将字符串“00”报告为非十六进制,因为零的计算结果为 False。

于 2014-05-13T20:09:49.407 回答
0

大多数解决方案都不能正确检查带前缀的字符串0x

>>> is_hex_string("0xaaa")  
False  
>>> is_hex_string("0x123")  
False  
>>> is_hex_string("0xfff")  
False  
>>> is_hex_string("fff")  
True  
于 2021-08-25T03:19:28.450 回答
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
于 2019-07-10T22:43:45.070 回答
0

简单的解决方案,如果您需要一个模式来验证前缀十六进制或二进制以及十进制

\b(0x[\da-fA-F]+|[\d]+|0b[01]+)\b

示例:https ://regex101.com/r/cN4yW7/14

然后int('0x00480065006C006C006F00200077006F0072006C00640021', 0)在 python 中做 6896377547970387516320582441726837832153446723333914657

基数 0 调用前缀猜测行为。这为我省去了很多麻烦。希望能帮助到你!

于 2020-05-22T22:06:01.583 回答
0

这是我的解决方案:

def to_decimal(s):
    '''input should be int10 or hex'''
    isString = isinstance(s, str)
    if isString:
        isHex = all(c in string.hexdigits + 'xX' for c in s)
        return int(s, 16) if isHex else int(s)
    else:
        return int(hex(s), 16)

a = to_decimal(12)
b = to_decimal(0x10)
c = to_decimal('12')
d = to_decimal('0x10')
print(a, b, c, d)
于 2022-01-28T05:06:21.470 回答
-1

在 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)

于 2014-12-04T15:27:06.717 回答