Подтвердить что ты не робот

Проверьте, является ли строка шестнадцатеричной

Я знаю, что самый простой способ - использовать регулярное выражение , но мне интересно, есть ли другие способы сделать эту проверку.

Зачем мне это нужно? Я пишу Python script, который читает текстовые сообщения (SMS) с карты SIM. В некоторых ситуациях появляется шестнадцатеричное сообщение, и мне нужно выполнить некоторую обработку для них, поэтому мне нужно проверить, является ли принятое сообщение шестнадцатеричным.

При отправке следующих SMS:

Hello world!

И мой script получает

00480065006C006C006F00200077006F0072006C00640021

Но в некоторых ситуациях я получаю обычные текстовые сообщения (не hex). Поэтому мне нужно сделать элемент if hex.

Я использую Python 2.6.5.

UPDATE:

Причиной этой проблемы являются (как-то) сообщения, отправленные мной, как hex, в то время как сообщения, отправленные оператором (информационные сообщения и объявления.), принимаются как обычная строка. Поэтому я решил сделать чек и убедиться, что у меня есть сообщение в правильном формате строки.

Дополнительные сведения. Я использую модем Huawei 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!'
4b9b3361

Ответ 1

(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 в начале, в то время как проверка символа по символу будет сбой. Кроме того, проверка на набор символов выполняется быстрее, чем строка символов, но это сомнительно, что это будет иметь значение с короткими строками SMS, если вы не обработаете много (много!) Из них последовательно, и в этом случае вы можете преобразовать stringhexditigs в набор с set(string.hexdigits).

Ответ 2

Вы можете:

  • проверьте, содержит ли строка только шестнадцатеричные цифры (0... 9, A... F)
  • попробуйте преобразовать строку в целое число и посмотреть, не сработает ли она.

Вот код:

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

Ответ 3

Я знаю, что в операторе упоминаются регулярные выражения, но я хотел бы предложить такое решение для полноты картины:

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

Решения Левона:

# 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() является победителем для более коротких строк.

Ответ 4

Другая опция:

def is_hex(s):
    hex_digits = set("0123456789abcdef")
    for char in s:
        if not (char in hex_digits):
            return False
    return True

Ответ 5

В большинстве предложенных решений не учитывается, что любое десятичное целое также может быть декодировано как шестнадцатеричное, поскольку набор десятичных цифр является подмножеством шестизначных цифр. Поэтому Python с радостью примет 123 и предположим, что он 0123 hex:

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

Ответ 6

Еще одно простое и короткое решение, основанное на преобразовании строки в набор и проверке на подмножество (не проверяет префикс "0x"):

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

Больше информации здесь.

Ответ 7

Это будет охватывать случай, если строка начинается с "0x" или "0X": [0x | 0X] [0-9a-fA-F]

d='0X12a'
all(c in 'xX' + string.hexdigits for c in d)
True

Ответ 8

Используя Python, вы хотите определить True или False, я бы использовал метод eumero is_hex по методу Levon one. Следующий код содержит getcha...

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

Он неверно сообщает строку '00' как не шестнадцатеричную, так как ноль имеет значение False.

Ответ 9

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

Ответ 10

Поскольку все приведенные выше регулярные выражения занимают примерно одинаковое количество времени, я предполагаю, что большую часть времени они были связаны с преобразованием строки в регулярное выражение. Ниже приведены данные, которые я получил при предварительной компиляции регулярного выражения.

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