ちょっと横道にそれて、与えられたデータを 1Byte ずつ 1BIT ごとのリストに変換する関数を作る。
要は、0xFF 0x01 を [[11111111][10000000]] に変換する関数である。この関数の用途については、後述する。(右から 1BIT目、2BIT目・・・、8BIT目 とする。)
def ConvDataToListOfBit(data) と命名するが、この関数の作成でいろいろと悩んでいたのが、日曜プログラマの限界か?(:b)
たとえば、data が PDF や EXE などのバイナリデータである場合、for c in data とすれば、c は 1Byte であるのだが、data が 文字列の場合、文字コードの違いにより c は 1Byte から 3Byte まで取り得る可能性がある。
そこで、一旦 data を unsigned char の整数型に変換する。
fmt = "%dB" % (len(data))
list_uchr_data= struct.unpack(fmt, data)
これにより、list_uchr_data は、文字コードによらず、0 から 255 までの整数値のみを取り扱うデータとなる。
また、len(list_uchr_data) は、data のバイト長となる。
さて、リスト内包表記を用いて、bit_values = [2**j for j in range(8)] なるものを定義する。
展開すると、[1, 2, 4, 8, 16, 32, 64, 128] となるのだが、これは、各 BIT が ON になったときの値である。すなわち、1BIT目のみが ON の場合は 1 であり、7BIT目のみが ON の場合は、64 である。
これを用いて、与えられた 1Byte データの各 BIT の ON/OFF をチェックする。
c_list_bits = []
for value in bit_values: c_list_bits.append((c & value) / value)
c_list_bits には、8 つの 1 もしくは 0 の数値がリスト形式で格納され、右から 1BIT、2BIT、・・・、8BIT目の ON/OFF を表す。
なお、bit_values = [2**j for j in range(7, -1, -1)] とすると、左右が逆転したリストを得ることができる。
これらを組み合わせて、先ほどの関数を作成する。
bit_values = [2**j for j in range(8)]
def ConvDataToListOfBit(data):
list_bits = []
fmt = "%dB" % (len(data))
list_uchr_data= struct.unpack(fmt, data)
for c in list_uchr_data:
c_list_bits = []
for value in bit_values: c_list_bits.append((c & value) / value)
list_bits.append(c_list_bits)
return list_bits
たとえば、utf-8 の「文字String」を変換すると、[[0, 1, 1, 0, 0, 1, 1, 1], [0, 1, 1, 0, 1, 0, 0, 1], [1, 1, 1, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 0, 1], [1, 1, 1, 0, 1, 0, 0, 1], [1, 1, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0], [0, 1, 0, 0, 1, 1, 1, 0], [1, 0, 0, 1, 0, 1, 1, 0], [0, 1, 1, 1, 0, 1, 1, 0], [1, 1, 1, 0, 0, 1, 1, 0]] となる。