struct.pack() 関数を使用すると、最大 64 ビットの整数をバイト文字列に変換できます。さらに大きな整数をパックする最も効率的な方法は何ですか? PyCrypto (num_to_bytes() を提供する) のような非標準モジュールへの依存関係を追加したくありません。
7 に答える
次のような意味ですか。
def num_to_bytes(num):
bytes = []
num = abs(num) # Because I am unsure about negatives...
while num > 0:
bytes.append(chr(num % 256))
num >>= 8
return ''.join(reversed(bytes))
def bytes_to_num(bytes):
num = 0
for byte in bytes:
num <<= 8
num += ord(byte)
return num
for n in (1, 16, 256, 257, 1234567890987654321):
print n,
print num_to_bytes(n).encode('hex'),
print bytes_to_num(num_to_bytes(n))
どちらが返されますか:
1 01 1
16 10 16
256 0100 256
257 0101 257
1234567890987654321 112210f4b16c1cb1 1234567890987654321
ネガをどうすればいいのかわかりません...ビットいじりにはあまり慣れていません。
編集:別の解決策 (私のテストでは約 30% 高速に実行されます):
def num_to_bytes(num):
num = hex(num)[2:].rstrip('L')
if len(num) % 2:
return ('0%s' % num).decode('hex')
return num.decode('hex')
def bytes_to_num(bytes):
return int(bytes.encode('hex'), 16)
投稿者が大きな整数をバイナリ文字列としてパックしたいと仮定すると、つまり、数値の桁ごとに 1 バイトのストレージを使用しません。これを行う1つの方法は次のようです。
import marshal
a = 47L
print marshal.dumps(a)
これは以下を出力します:
'l\x01\x00\x00\x00/\x00'
今のところ、これらのビットを解釈する方法を理解しているとは言えません...
これは少しハッキーですが、16進文字列表現を経由して、16進コーデックでバイナリに進むことができます。
>>> a = 2**60
>>> a
1152921504606846976L
>>> hex(a)
'0x1000000000000000L'
>>> hex(a).rstrip("L")[2:].decode('hex')
'\x10\x00\x00\x00\x00\x00\x00\x00' # 8bytes, as expected.
>>> int(_.encode('hex'), 16)
1152921504606846976L
16進コーデックには偶数桁が必要なため、少し壊れます。そのためにパディングする必要があり、負の数を処理するためにフラグを設定する必要があります。一般的なパック/アンパックは次のとおりです。
def pack(num):
if num <0:
num = (abs(num) << 1) | 1 # Hack - encode sign as lowest bit.
else:
num = num << 1
hexval = hex(num).rstrip("L")[2:]
if len(hexval)%2 ==1: hexval = '0' + hexval
return hexval.decode('hex')
def unpack(s):
val = int(s.encode('hex'), 16)
sign = -1 if (val & 1) else 1
return sign * (val>>1)
for i in [10,4534,23467, 93485093485, 2**50, 2**60-1, -1, -20, -2**60]:
assert unpack(pack(i)) == i
パディングなどのすべての手間が必要ですが、手巻きのソリューションよりもはるかに優れているかどうかはわかりません。
数値を表すのに必要な数のバイトだけを使用したいということですか? たとえば、番号が次の場合:
- 255 以下では 1 バイトしか使用しません
- 65535以下 2バイト
- 16777215以下 3バイト
- などなど
Psion PDA では、通常、最初のバイトを読み取り、最上位ビットが設定されているかどうかを検出し、設定されている場合は別のバイトを読み取るパッキング スキームがいくつかあります。そうすれば、「完全な」数を読み取るまでバイトを読み続けることができます。通常、数値ごとに 1 つか 2 バイトしか使用しないため、扱う数値のほとんどがかなり小さい場合、このシステムは非常にうまく機能します。
別の方法は、使用された合計バイト数を表す 1 (または複数) バイトを使用することですが、その時点では基本的に Python の文字列です。つまり、base-256 の数字の文字列です。
コメントで S.Lott が示唆しているように、数値を文字列に変換し、その文字列をパックするだけです。例えば、
x = 2 ** 12345
struct.pack("40s", str(x))