web-dev-qa-db-ja.com

可変サイズのバイト配列を整数/ longに変換します

(ビッグエンディアン)可変サイズのバイナリバイト配列を(符号なし)整数/長整数型に変換するにはどうすればよいですか?例として、'\x11\x34'、4404を表します

今、私は使用しています

def bytes_to_int(bytes):
  return int(bytes.encode('hex'), 16)

これは小さく、多少読みやすいですが、おそらくあまり効率的ではありません。より良い(より明白な)方法はありますか?

34
goncalopp

Pythonは従来、Cには大きすぎる「ビッグエンディアンCレイアウトの数値」をあまり使用していません。(2バイト、4バイト、または8バイトの数値を扱う場合は、_struct.unpack_が答えです。)

しかし、十分な人々はPython 3.2がメソッドを追加しました _int.from_bytes_ これはまさにあなたが望むことをする1つの明白な方法がないことにうんざりしました:

_int.from_bytes(b, byteorder='big', signed=False)
_

残念ながら、古いバージョンのPythonを使用している場合、これはありません。それで、あなたにはどんなオプションがありますか? (明らかなものに加えて、3.2へのアップデート、またはより良い、3.4…)


まず、コードがあります。 _binascii.hexlify_は.encode('hex')よりもスペルの良い方法だと思います。なぜなら、「encode」はバイト文字列のメソッド(Unicode文字列とは対照的に)で常に少し奇妙に見え、実際にはPython 3.で追放されました。しかし、それ以外の場合は、非常に読みやすく、私にとっては明らかです。そして、それは非常に高速である必要があります。 Cでのループと算術(少なくともCPython)。これは一般にPythonよりも1桁または2倍高速です。bytearrayが大きすぎて文字列の割り当て自体がコストがかかる場合を除き、ここでパフォーマンスを心配します。

または、ループで実行することもできます。しかし、それはより冗長になり、少なくともCPythonでははるかに遅くなります。

暗黙のループの明示的なループを排除しようとすることもできますが、それを行うための明白な関数はreduceです。これはコミュニティの一部によって非Python的であると見なされます。各バイト。

ループまたはreduceを展開するには、8バイトのチャンクに分割して_struct.unpack_from_をループするか、大きなstruct.unpack('Q'*len(b)//8 + 'B' * len(b)%8)を実行してループしますが、読みにくく、おそらくそれほど速くはありません。

NumPyを使用することもできますが、64または128ビットよりも大きくなると、すべてをPythonオブジェクトに変換することになります。

ですから、あなたの答えが最良の選択肢だと思います。


最も明らかな手動変換と比較するタイミングを次に示します。

_import binascii
import functools
import numpy as np

def hexint(b):
    return int(binascii.hexlify(b), 16)

def loop1(b):
    def f(x, y): return (x<<8)|y
    return functools.reduce(f, b, 0)

def loop2(b):
    x = 0
    for c in b:
        x <<= 8
        x |= c
    return x

def numpily(b):
    n = np.array(list(b))
    p = 1 << np.arange(len(b)-1, -1, -1, dtype=object)
    return np.sum(n * p)
_

_In [226]: b = bytearray(range(256))

In [227]: %timeit hexint(b)
1000000 loops, best of 3: 1.8 µs per loop

In [228]: %timeit loop1(b)
10000 loops, best of 3: 57.7 µs per loop

In [229]: %timeit loop2(b)
10000 loops, best of 3: 46.4 µs per loop

In [283]: %timeit numpily(b)
10000 loops, best of 3: 88.5 µs per loop
_

Python 3.4:

_In [17]: %timeit hexint(b)
1000000 loops, best of 3: 1.69 µs per loop

In [17]: %timeit int.from_bytes(b, byteorder='big', signed=False)
1000000 loops, best of 3: 1.42 µs per loop
_

だから、あなたの方法はまだかなり速いです...

55
abarnert

関数 struct.unpack(...) は必要なことを行います。

2
Curd