2016-04-13 23 views
1

9個の数字を持つデータパッケージをアンパックすると、各数字は3の2の補数形式になります。したがって私のパッケージの27バイト。 私は私が4つのバイトの数字でstruct.unpackを使用することができますが、16各3バイトが2の補数である27バイトの配列を数値に変換する方法は?

作品で私の数字を掛け合わが、私は持っているだろう4バイトの2の補数の場合は16

data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 

a = a[:3] + [0] + a[3:6] + [0] + a[6:9] + [0] + a[9:12] + [0] + a[12:15] + [0] + a[15:18] + a[18:21] + [0] + [0] + a[21:24] + [0] + a[24:27] + [0] 

ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8 = struct.unpack('>lllllllll', bytearray(a)) 

によって出力を乗算され、これを持っています負の数の前に0xFFを、正の数の前に0x00を追加します。

これを実行するスマートな方法はありますか?それを行う最も効率的な方法は何ですか?

+0

もしそれ倍数出力により、 16、なぜちょうど 'ch0 >> 4'をやって、なぜ' ch0'を2^4 = 16で割りますか? –

答えて

1

3バイトの値を単一の符号付きバイトと符号なし(16ビット)のshortとして解析することをお勧めします。数学演算子を使用して、それらを1つの数に再結合することができます。

ここで(bytearrayまたはbytes文字列としてsを与えられた)ワンステップでそれをすべて行い、リスト内包表記です:私はstruct.unpackを使用して、上記のように

[(a<<16) + b for a, b in zip(*([iter(struct.unpack('>'+'bH'*9, s))]*2))] 

最も内側の部分は、構文解析を行います。次に、同じイテレータの2つのコピーにzipを使用して、バイト/ショートペアを取得します(これは素晴らしいテクニックです)。それらを一緒に追加する前に、バイトを16ビット左シフトします。

すでに符号なし整数としてバイトを持っている場合は、struct.unpackを使用する必要はなく、3つのグループで直接結合することもできます(ロジックのほんの少しで、最初のバイト):

[((a if a < 128 else a-256)<<16) + (b<<8) + c for a, b, c in zip(*([iter(data)]*3))] 
+0

提示されたソリューションから、あなたのものが最も速かった。私は '%timeit'を実行し、' 100000ループ、3:4.85μs/ループ 'を得ました。私はまだあなたが一緒に戻って値を持っている方法で少し失われています。あなたがそれについてもっと詳しく話すことができれば。もう一つ。私は 's = bytearray(データ)'を使用する前に(後で参照するために) – nico

+1

うん、 's'はその中のデータでバイトストリングまたはbytearrayする必要があります。 1つのトリッキーなビット(私はそのように述べた)は、 'struct.unpack'によって返されたタプルからバイト・ショート・ペアを取り出しています。 'it(iter(x))* 2))'ビットは、反復可能な 'x'からペアを得るためのきれいなレシピです(' itertools'レシピで 'グルーパー)。ペアで解析された値を取得したら、それらを単一の整数値にすることは難しくありません。バイト(符号付きとして解析済み)を左シフトし、符号なしshortに追加します。 – Blckknght

2

あなたは4分の1を作るために3番目のバイトの符号ビットを拡張するためのヘルパー関数を使用することができます。

import struct 

data = [0x00, 0x00, 0x00, 
     0xff, 0xff, 0xff, 
     0x00, 0x00, 0x02, 
     0xff, 0xff, 0xfd, 
     0x00, 0x00, 0x04, 
     0xff, 0xff, 0xfb, 
     0x00, 0x00, 0x06, 
     0xff, 0xff, 0xf9, 
     0x00, 0x00, 0x08] 

signed = lambda v: [0xff if v & 0x80 else 0x00,] 
ch = [struct.unpack('>i', ''.join(map(chr, signed(data[i]) + data[i:i+3])))[0] 
                for i in range(0, len(data), 3)] 

print(ch) # -> [0, -1, 2, -3, 4, -5, 6, -7, 8] 
+0

ありがとうございます。ラムダ関数の本当にいい使い方。私は '%timeit'を実行して、' 100000ループ、最善の3:4.85μs/ループ 'を提供しました。 @Blckknghtソリューションの10倍遅くなります。 – nico

+0

出力に間違ったペーストがあり、編集できませんでした。あなたの関数は '' 100000ループ、最高3:15.2μs/ループ 'で実行されます – nico

+1

したがって、それはBlckknghtの約3倍です。数多くの関数呼び出しが(ラムダのものに加えて)作られていることを考えると、驚くことではありません。 – martineau

1

あなたはクラスのメソッドを使用するのPython 3を、使用している場合はint.from_bytes 。これは、任意の長さのバイトクラスターを扱うことができます。

#!python3 
data = bytes([0,0,0,0xff,0xff,0xff,0,0,1,0xff,0xff,0xfe,0,0,3,0xff,0xff,0xfd,0,0,4]) 
for i in range(0,len(data),3): 
    print(int.from_bytes(data[i:i+3],'big',signed=True)) 

出力:

0 
-1 
1 
-2 
3 
-3 
4 

あなたにもそれらを生成する必要がある場合、またto_bytesあり:

>>> (-2).to_bytes(3,'big',signed=True) 
b'\xff\xff\xfe' 
+0

興味深い解決策。しかし、私は2.7を使用する必要があります。しかし、私は将来これを念頭に置いています。 – nico

関連する問題