Sprawdź, czy łańcuch jest szesnastkowy
Wiem, że najprostszym sposobem jest użycie wyrażenia regularnego , ale zastanawiam się, czy są inne sposoby, aby to sprawdzić.
Po co mi to? Piszę skrypt Pythona, który odczytuje wiadomości tekstowe (SMS) z karty SIM. W niektórych sytuacjach pojawiają się wiadomości szesnastkowe i muszę je przetworzyć, więc muszę sprawdzić, czy otrzymana wiadomość jest szesnastkowa.Kiedy wysyłam następujący SMS:
Hello world!
I mój skrypt otrzymuje
00480065006C006C006F00200077006F0072006C00640021
Ale w w niektórych sytuacjach otrzymuję zwykłe wiadomości tekstowe (nie szesnastkowe). Więc muszę wykonać if hex control.
Używam Pythona 2.6.5.
Aktualizacja:
Powodem tego problemu jest to, (jakoś) wiadomości, które wysłałem, są odbierane jako hex
, podczas gdy wiadomości wysyłane przez operatora (wiadomości informacyjne i reklamy.) są odbierane jako zwykły ciąg znaków. Więc postanowiłem sprawdzić i upewnić się, że mam wiadomość w prawidłowym formacie ciągu.
Kilka dodatkowych szczegółów : używam Modem 3G Huawei i PyHumod {[9] } do odczytu danych z karty sim.
możliwe najlepsze rozwiązanie mojej sytuacji:
Najlepszym sposobem obsługi takich łańcuchów jest użycie a2b_hex
(vel unhexlify
) i utf-16 big endian encoding
(jak wspomniał @ JonasWielicki):
from binascii import unhexlify # unhexlify is another name of a2b_hex
mystr = "00480065006C006C006F00200077006F0072006C00640021"
unhexlify(mystr).encode("utf-16-be")
>> u'Hello world!'
8 answers
(1) użycie int () Działa do tego ładnie, a Python sprawdza wszystko za Ciebie:)
int('00480065006C006C006F00200077006F0072006C00640021', 16)
6896377547970387516320582441726837832153446723333914657L
Zadziała. W przypadku awarii otrzymasz wyjątek ValueError
.
krótki przykład:
int('af', 16)
175
int('ah', 16)
...
ValueError: invalid literal for int() with base 16: 'ah'
(2) alternatywą byłoby przemieszczenie danych i upewnienie się, że wszystkie znaki mieszczą się w zakresie0..9
i a-f/A-F
. string.hexdigits
('0123456789abcdefABCDEF'
) jest to przydatne, ponieważ zawiera zarówno duże jak i małe litery cyfry.
import string
all(c in string.hexdigits for c in s)
Zwróci True
lub False
na podstawie ważności Twoich danych w łańcuchu s
.
krótki przykład:
s = 'af'
all(c in string.hexdigits for c in s)
True
s = 'ah'
all(c in string.hexdigits for c in s)
False
uwagi:
Jak @ScottGriffiths zauważa poprawnie w komentarzu poniżej, podejście int()
będzie działać, jeśli twój ciąg znaków zawiera 0x
na początku, podczas gdy sprawdzanie znak po znaku nie powiedzie się z tym. Ponadto sprawdzanie przed zestawem znaków jest szybsze niż łańcuch z znaków, ale wątpliwe, że będzie to miało znaczenie przy krótkich ciągach SMS, chyba że przetworzysz wiele (wiele!) z nich w kolejności, w którym to przypadku można przekonwertować stringhexditigs do zbioru z set(string.hexdigits)
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-21 14:16:43
Możesz:
- sprawdź, czy łańcuch zawiera tylko cyfry szesnastkowe (0...9,A...F)
- spróbuj przekonwertować łańcuch na liczbę całkowitą i sprawdź, czy się nie powiedzie.
Oto kod:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-21 13:59:14
Znam op wspomniane wyrażenia regularne , ale chciałem dodać takie rozwiązanie ze względu na kompletność:
def is_hex(s):
return re.fullmatch(r"^[0-9a-fA-F]$", s or "") is not None
Wydajność
W celu oceny wydajności różnych rozwiązań zaproponowanych tutaj, użyłem modułu timeit Pythona. Ciągi wejściowe są generowane losowo dla trzech różnych długości, 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
Inne odpowiedzi to wariacje z tych dwóch. Użycie wyrażenia regularnego:
# re.fullmatch(r'^[0-9a-fA-F]$', s or '')
10: 0.725040541990893
100: 0.7184272820013575
1000: 0.7190397029917222
Wybór właściwego rozwiązania zależy więc od długości łańcucha wejściowego i tego, czy wyjątki mogą być bezpiecznie obsługiwane. Wyrażenie regularne z pewnością obsługuje duże struny znacznie szybciej (i nie rzuca ValueError
na przepełnienie), ale int()
jest zwycięzcą dla krótszych strun.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:25
Inna opcja:
def is_hex(s):
hex_digits = set("0123456789abcdef")
for char in s:
if not (char in hex_digits):
return False
return True
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 18:48:35
Większość rozwiązań zaproponowanych powyżej nie bierze pod uwagę, że każda liczba dziesiętna może być również dekodowana jako hex, ponieważ zestaw cyfr dziesiętnych jest podzbiorem zestawu cyfr szesnastkowych. Więc Python z radością weźmie {[2] } i założy, że to 0123
hex:
>>> int('123',16)
291
Może to wydawać się oczywiste, ale w większości przypadków będziesz szukał czegoś, co było kodowane szesnastkowo, np. hash, a nie czegoś, co może być dekodowane szesnastkowo. Więc chyba bardziej solidne rozwiązanie powinno również sprawdzić równomierną długość z ciągu sześciokątnego:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-29 19:42:24
W Python3 próbowałem:
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 ''
Powinno być lepiej niż droga: int (x, 16)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-04 15:40:58
Używając Pythona, którego szukasz do określenia True lub False, użyłbym metody is_hex eumero zamiast metody Levona. Poniższy kod zawiera "gotcha"...
if int(input_string, 16):
print 'it is hex'
else:
print 'it is not hex'
Niepoprawnie zgłasza łańcuch " 00 " jako , a nie hex, ponieważ zero jest obliczane jako False.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 06:55:36
To obejmie przypadek, jeśli łańcuch zaczyna się od '0x' lub '0x': [0x|0x] [0-9A-fA-F]
d='0X12a'
all(c in 'xX' + string.hexdigits for c in d)
True
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-16 21:44:32