Pythonでバイト文字列をintに変換する方法
Pythonでバイト文字列をintに変換するにはどうすればよいですか?
次のように言います:'y\xcc\xa6\xbb'
私はそれを行う賢い/愚かな方法を思いつきました:
sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))
私は、これをより簡単に行うために、組み込みのものまたは標準ライブラリに何かがある必要があることを知っています...
これは、int(xxx、16)を使用できる 16進数の文字列の変換 とは異なりますが、実際のバイト値の文字列を変換したいです。
更新:
別のモジュールをインポートする必要がないため、ジェームズの答えが少し良くなりますが、グレッグの方法はより高速です:
>>> from timeit import Timer
>>> Timer('struct.unpack("<L", "y\xcc\xa6\xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> Timer("int('y\xcc\xa6\xbb'.encode('hex'), 16)").timeit()
1.1432669162750244
私のハッキーな方法:
>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit()
2.8819329738616943
さらに更新:
誰かが別のモジュールをインポートする際の問題点をコメントで尋ねました。さて、モジュールのインポートは必ずしも安価ではないので、見てください。
>>> Timer("""import struct\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""").timeit()
0.98822188377380371
モジュールをインポートするコストを含めると、この方法の利点のほとんどすべてが無効になります。これには、ベンチマーク実行全体で1回インポートするだけの費用が含まれると考えています。毎回強制的にリロードするときに何が起こるか見てください:
>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794
言うまでもなく、1回のインポートでこのメソッドを多数実行する場合は、これに比例して問題が少なくなります。 CPUよりもおそらくI/Oコストであるため、特定のマシンの容量と負荷の特性に依存する可能性があります。
struct モジュールを使用してこれを行うこともできます。
>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Python 3.2以降では、使用します
>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='big')
2043455163
または
>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='little')
3148270713
バイト文字列の エンディアンネス に従って。
これは、任意の長さのバイト文字列整数、およびsigned=True
を指定することによる2の補数の符号付き整数に対しても機能します。 from_bytes
のドキュメント を参照してください。
Gregが言ったように、バイナリ値を処理する場合はstructを使用できますが、「16進数」だけでバイト形式の場合は、次のように変換するだけです。
s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)
...これは次と同じです:
num = struct.unpack(">L", s)[0]
...ただし、任意のバイト数で機能します。
次の関数を使用して、int、hex、およびbytes間でデータを変換します。
def bytes2int(str):
return int(str.encode('hex'), 16)
def bytes2hex(str):
return '0x'+str.encode('hex')
def int2bytes(i):
h = int2hex(i)
return hex2bytes(h)
def int2hex(i):
return hex(i)
def hex2int(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]
if len(h) % 2:
h = "0" + h
return int(h, 16)
def hex2bytes(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]
if len(h) % 2:
h = "0" + h
return h.decode('hex')
ソース: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]
警告:上記はプラットフォーム固有のものです。 「I」指定子とstring-> int変換のエンディアンは、特定のPython実装に依存します。しかし、一度に多くの整数/文字列を変換したい場合、配列モジュールはそれをすばやく行います。
Python 2.xでは、フォーマット指定子<B
を符号なしバイトに使用し、<b
を符号付きバイトにstruct.unpack
/struct.pack
とともに使用できます。
例えば:
x
= '\xff\x10\x11'
とする
data_ints = struct.unpack('<' + 'B'*len(x), x) # [255, 16, 17]
そして:
data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'
その*
が必要です!
書式指定子のリストについては、 https://docs.python.org/2/library/struct.html#format-characters を参照してください。
>>> reduce(lambda s, x: s*256 + x, bytearray("y\xcc\xa6\xbb"))
2043455163
テスト1:逆:
>>> hex(2043455163)
'0x79cca6bb'
テスト2:バイト数> 8:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAA"))
338822822454978555838225329091068225L
テスト3:1ずつ増加:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAB"))
338822822454978555838225329091068226L
テスト4:1バイトを追加し、「A」と言います。
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))
86738642548474510294585684247313465921L
テスト5:256で割る:
>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256
338822822454978555838225329091068226L
結果はテスト4の結果と同じです。
バージョン3.2以降の場合、int.from_bytesが最適なソリューションです。 「struct.unpack」ソリューションには文字列が必要なため、バイトの配列には適用されません。別のソリューションを次に示します。
def bytes2int( tb, order='big'):
if order == 'big': seq=[0,1,2,3]
Elif order == 'little': seq=[3,2,1,0]
i = 0
for j in seq: i = (i<<8)+tb[j]
return i
hex(bytes2int([0x87、0x65、0x43、0x21]))は、 '0x87654321'を返します。
ビッグエンディアンとリトルエンディアンを処理し、8バイトで簡単に変更可能
上記のように、structのunpack
関数を使用するのが良い方法です。独自の機能を実装する場合は、別の解決策があります。
def bytes_to_int(bytes):
result = 0
for b in bytes:
result = result * 256 + int(b)
return result
Python 2.xで動作する任意の長さのバイトシーケンスの解決策を見つけるのに苦労していました。最後に私はこれを書きました。文字列変換を実行するので少しハックしますが、動作します。
Python 2.xの関数、任意の長さ
def signedbytes(data):
"""Convert a bytearray into an integer, considering the first bit as
sign. The data must be big-endian."""
negative = data[0] & 0x80 > 0
if negative:
inverted = bytearray(~d % 256 for d in data)
return -signedbytes(inverted) - 1
encoded = str(data).encode('hex')
return int(encoded, 16)
この関数には2つの要件があります。
入力
data
はbytearray
である必要があります。次のような関数を呼び出すことができます。s = 'y\xcc\xa6\xbb' n = signedbytes(s)
データはビッグエンディアンである必要があります。リトルエンディアンの値がある場合は、最初にそれを逆にする必要があります。
n = signedbytes(s[::-1])
もちろん、これは任意の長さが必要な場合にのみ使用してください。それ以外の場合は、より標準的な方法(例:struct
)を使用してください。
私がしばらく使用してきたarray.arrayを利用したかなり高速な方法:
事前定義変数:
offset = 0
size = 4
big = True # endian
arr = array('B')
arr.fromstring("\x00\x00\xff\x00") # 5 bytes (encoding issues) [0, 0, 195, 191, 0]
to int:(読み取り)
val = 0
for v in arr[offset:offset+size][::pow(-1,not big)]: val = (val<<8)|v
intから:(書き込み)
val = 16384
arr[offset:offset+size] = \
array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]
ただし、これらはより高速になる可能性があります。
編集:
いくつかの数値について、reduce()
と比較した読み取り時の安定した平均を示すパフォーマンステスト(Anaconda 2.3.0)があります。
========================= byte array to int.py =========================
5000 iterations; threshold of min + 5000ns:
______________________________________code___|_______min______|_______max______|_______avg______|_efficiency
⣿⠀⠀⠀⠀⡇⢀⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡀⠀⢰⠀⠀⠀⢰⠀⠀⠀⢸⠀⠀⢀⡇⠀⢀⠀⠀⠀⠀⢠⠀⠀⠀⠀⢰⠀⠀⠀⢸⡀⠀⠀⠀⢸⠀⡇⠀⠀⢠⠀⢰⠀⢸⠀
⣿⣦⣴⣰⣦⣿⣾⣧⣤⣷⣦⣤⣶⣾⣿⣦⣼⣶⣷⣶⣸⣴⣤⣀⣾⣾⣄⣤⣾⡆⣾⣿⣿⣶⣾⣾⣶⣿⣤⣾⣤⣤⣴⣼⣾⣼⣴⣤⣼⣷⣆⣴⣴⣿⣾⣷⣧⣶⣼⣴⣿⣶⣿⣶
val = 0 \nfor v in arr: val = (val<<8)|v | 5373.848ns | 850009.965ns | ~8649.64ns | 62.128%
⡇⠀⠀⢀⠀⠀⠀⡇⠀⡇⠀⠀⣠⠀⣿⠀⠀⠀⠀⡀⠀⠀⡆⠀⡆⢰⠀⠀⡆⠀⡄⠀⠀⠀⢠⢀⣼⠀⠀⡇⣠⣸⣤⡇⠀⡆⢸⠀⠀⠀⠀⢠⠀⢠⣿⠀⠀⢠⠀⠀⢸⢠⠀⡀
⣧⣶⣶⣾⣶⣷⣴⣿⣾⡇⣤⣶⣿⣸⣿⣶⣶⣶⣶⣧⣷⣼⣷⣷⣷⣿⣦⣴⣧⣄⣷⣠⣷⣶⣾⣸⣿⣶⣶⣷⣿⣿⣿⣷⣧⣷⣼⣦⣶⣾⣿⣾⣼⣿⣿⣶⣶⣼⣦⣼⣾⣿⣶⣷
val = reduce( shift, arr ) | 6489.921ns | 5094212.014ns | ~12040.269ns | 53.902%
これは生のパフォーマンステストであるため、エンディアンのパウフリップは省略されています。
示されているshift
関数は、forループと同じシフト演算操作を適用します。また、arr
は、dict
に次いで最も高速な反復パフォーマンスを備えているため、ただarray.array('B',[0,0,255,0])
です。
おそらく、効率は平均時間に対する精度によって測定されることに注意する必要があります。