正規表現 を使用するのが最も簡単な方法であることは知っていますが、このチェックを行う方法は他にあるのでしょうか。
なぜこれが必要なのですか? Python [〜#〜] sim [〜#〜] カードからテキストメッセージ(SMS)を読み取るスクリプトを記述しています。状況によっては、16進メッセージ到着し、それらの処理を行う必要があるため、受信したメッセージが16進数かどうかを確認する必要があります。
次のSMSを送信する場合:
Hello world!
そして、私のスクリプトは受け取ります
00480065006C006C006F00200077006F0072006C00640021
しかし、状況によっては、通常のテキストメッセージ(16進数ではない)を受け取ります。だから、if hexコントロールを行う必要があります。
私はPython 2.6.5。
更新:
その問題の理由は、(何らかの理由で)私が送信したメッセージはhex
として受信され、オペレーターによって送信されたメッセージ(情報メッセージと広告)は通常の文字列として受信されるためです。そこで、チェックを行い、メッセージが正しい文字列形式であることを確認することにしました。
いくつかの追加の詳細:私はHuawei 3Gモデムと PyHumod を使用してSIMカードからデータを読み取ります。
私の状況に対する最良の解決策:
そのような文字列を処理する最良の方法は、a2b_hex
(別名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!'
(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)alternativeは、データを走査し、すべての文字が範囲内に収まることを確認します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が以下のコメントで正しく注記しているように、文字列に最初に0x
が含まれている場合、int()
アプローチは機能しますが、文字ごとのチェックはこれで失敗します。また、setの文字に対するチェックはstringの文字よりも高速ですが、これは短い=で問題になることは疑わしいですSMS文字列。ただし、文字列の多くを(多く!)シーケンスで処理しない限り、set(string.hexdigits)
でstringhexditigsをセットに変換できます。
あなたはできる:
コードは次のとおりです。
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
私は 正規表現 に言及したopを知っていますが、完全を期すためにそのようなソリューションに貢献したかったのです:
_def is_hex(s):
return re.fullmatch(r"^[0-9a-fA-F]$", s or "") is not None
_
パフォーマンス
ここで提案されているさまざまなソリューションのパフォーマンスを評価するために、Pythonの timeit モジュールを使用しました。入力文字列は、3つの異なる長さ_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
_
他の答えは、これら2つのバリエーションです。正規表現を使用する:
_# re.fullmatch(r'^[0-9a-fA-F]$', s or '')
10: 0.725040541990893
100: 0.7184272820013575
1000: 0.7190397029917222
_
したがって、適切なソリューションを選択するかどうかは、入力文字列の長さと、例外を安全に処理できるかどうかに依存します。正規表現は確かに大きな文字列をはるかに高速に処理します(そして、オーバーフロー時にValueError
をスローしません)が、int()
が短い文字列の勝者です。
別のオプション:
def is_hex(s):
hex_digits = set("0123456789abcdef")
for char in s:
if not (char in hex_digits):
return False
return True
上記で提案されたソリューションのほとんどは、10進数字セットが16進数字セットのサブセットであるため、10進整数も16進としてデコードされることを考慮していません。だからPythonは喜んで123
そして、それが0123
hex:
>>> int('123',16)
291
これは当たり前のように聞こえるかもしれませんが、ほとんどの場合、実際に16進数でエンコードされたものを探しています。 canが16進数でデコードされるものではなく、ハッシュ。そのため、おそらくより堅牢なソリューションでは、16進文字列の長さが偶数であることも確認する必要があります。
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
サブセットの設定とチェックのための文字列の変換に基づくもう1つの簡単で短いソリューション(「0x」プレフィックスはチェックしません):
import string
def is_hex_str(s):
return set(s).issubset(string.hexdigits)
詳細情報 こちら 。
これは、文字列が「0x」または「0X」で始まる場合に対応します。[0x | 0X] [0-9a-fA-F]
d='0X12a'
all(c in 'xX' + string.hexdigits for c in d)
True
上記のすべての正規表現はほぼ同じ時間を要したので、ほとんどの時間は文字列を正規表現に変換することに関連していたと思います。以下は、正規表現をプリコンパイルするときに取得したデータです。
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
Pythonを使用して、TrueまたはFalseを決定しようとしている場合、Levonのメソッド1ではなくeumeroのis_hexメソッドを使用します。次のコードには、...
if int(input_string, 16):
print 'it is hex'
else:
print 'it is not hex'
ゼロはFalseと評価されるため、文字列 '00'をnot hexとして誤って報告します。
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)