バイト文字列をintに変換する方法は?


162

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進数の文字列の変換とは異なりますが、実際のバイト値の文字列を変換したいと思います。

更新:

別のモジュールをインポートする必要がないので、Jamesの答えが少し良いのが好きですが、Gregの方法はより高速です。

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

モジュールのインポートのコストを含めると、この方法の利点のほとんどすべてが無効になります。これには、ベンチマークの実行全体で一度だけインポートする費用が含まれると思います。毎回リロードを強制するとどうなるか見てください:

>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794

言うまでもなく、1回のインポートでこのメソッドを多数実行している場合は、比例して問題が少なくなります。また、特定のマシンの容量と負荷特性に依存する可能性があるため、CPUよりもI / Oコストになる可能性があります。


標準ライブラリから何かをインポートすることは悪いことです、なぜですか?


26
あなたの「更なるアップデート」は奇妙です...なぜそんなに頻繁にモジュールをインポートするのですか?

5
これは古い質問です。しかし、他の人のために比較を最新に保ちたい場合:私のコンピューターでは機械式カタツムリの答え(int.from_bytes)が優れstruct.unpackています。次はもっと読みやすいimoです。
magu_

回答:


110

これを行うには、構造体モジュールを使用することもできます。

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L

3
警告:64ビットのPythonビルドでは、「L」は実際には8バイト(4ではない)であるため、失敗する可能性があります。
ラファウDowgird

12
Rafał:そうではありません。Gregは<を使用していたため、ドキュメントによれば、Lは標準サイズ(4)です。または「=」。」docs.python.org/library/struct.html#format-characters
アンドレ・ラズロ

59
この回答は、任意の長さのバイナリ文字列では機能しません。
amcnabb 2013

4
型には特定のサイズがあり、任意の長さのバイナリ文字列では機能しません。各アイテムのタイプがわかっている場合は、forループを設定してそれを処理できます。
Joshua Olson

2
「L」は実際にはuint32(4バイト)です。私の場合のように8バイトが必要な場合は、「Q」-> uint64を使用します。"l"-> int32およびq-> int64
ntg

319

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

バイト文字列のエンディアンに従って。

これは、任意の長さのバイト文字列整数、およびを指定することにより2の補数の符号付き整数でも機能しsigned=Trueます。のドキュメントをfrom_bytes参照してください。


@eriはどのくらい遅いですか?以前はstructを使用していましたが、py3に行ったときにint.from_bytesに変換しました。私はシリアルデータを受信して​​いるときに毎秒このメソッドを呼び出しているので、高速化は歓迎されます。私はこれを見てきました
Naib '25 / 12/25

@Naib、os.urandom(4)私のCPUのバイト** 1.4 µs **(struct)と** 2.3 µs **(int.from_bytes)の場合。python 3.5.2
eri

5
@eriいくつかのCRCメソッドの評価に使用したtimeitスクリプトを復活させました。4つの実行1)構造体2)int.from_bytes 3)#1としてcythonをコンパイル、4)#2としてcythonをコンパイル 構造体で330ns、intで1.14us(cythonはおそらく両方で20nsのスピードアップを与えました...)私は元に戻しているように見えます:)これは時期尚早な最適化ではなく、特に投稿する100万のサンプルでいくつかの厄介なボトルネックに直面しています-パーツをノックオフして処理しています。
Naib、

66

Gregが言ったように、バイナリ値を扱う場合はstructを使用できますが、「16進数」だけがバイト形式である場合は、次のように変換することをお勧めします。

s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)

...これは次と同じです:

num = struct.unpack(">L", s)[0]

...ただし、バイト数に関係なく機能します。


3
「バイナリ値」と「「16進数」ですが、バイト形式」の違いは何ですか???????

「ヘルプ構造体」を参照してください。例えば。"001122334455" .decode( 'hex')は、構造体を使用して数値に変換できません。
James Antill、

3
ちなみに、この回答は、整数がビッグエンディアンのバイトオーダーでエンコードされていることを前提としています。リトルエンディアンの順序については、実行しますint(''.join(reversed(s)).encode('hex'), 16)
amcnabb

1
良いですが、これは遅くなります!あなたがPythonでコーディングしているなら、それは本当に問題ではないと思います。
MattCochrane 2015年

8

次の関数を使用して、データをint、hex、byteに変換します。

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


6
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]

警告:上記はプラットフォーム固有のものです。"I"指定子とstring-> int変換のエンディアンは、特定のPython実装に依存しています。しかし、一度に多くの整数/文字列を変換したい場合、配列モジュールはそれをすばやく行います。


5

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参照してください


3
>>> 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:「A」と言う1バイトを追加します。

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))
86738642548474510294585684247313465921L

テスト5:256で除算:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256
338822822454978555838225329091068226L

結果は予想通り、テスト4の結果と同じです。


1

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


1

int.from_bytesは、バージョンが3.2以上の場合に最適なソリューションです。"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バイトに簡単に変更できます。


1

上記のようにunpackstructの関数を使用することは良い方法です。独自の関数を実装したい場合は、別の解決策があります:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result

これは、バイトに変換された負の数では機能しません。
マリア

1

Python 3では、バイト文字列を整数(0..255)のリストに簡単に変換できます。

>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]

0

私がしばらく使ってきた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]

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

ただし、これらはより高速になる可能性があります。

編集:
一部の数値については、次のパフォーマンステスト(Anaconda 2.3.0)と比較して、読み取り時の安定した平均を示していますreduce()

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

これは生のパフォーマンステストであるため、エンディアンパウフリップは省略されます。示す関数はforループと同じシフト論理和演算を適用し、ちょうどである、それは隣に最速の反復の性能を持っているよう。
shiftarrarray.array('B',[0,0,255,0])dict

また、効率は平均時間の精度で測定されることにも注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.