Pythonのバイナリデータの扱いについて
画面表示
printf
a = 15 print("%02x" % a) #=> 0f (16進数で表示) b = "A" print(ord(b)) #=> 65 (10進数で表示) print("0x%02x\n" % ord(b)) #=> 0x41 c = "a" print(ord(c)) #=> 97 print("0x%02x\n" % ord(c)) #=> 0x61 print(hex(ord(c)) #=> 0x61
ord
ord関数は、長さ1の文字列が8ビット文字列なら、バイト値を返します。
data = 'B' print(ord(data)) #=> 66ord関数と、chr関数の機能は逆になります。 そのため、chr(ord('B'))と、 ord(chr(66)) は、何も処理していないのと同じになります。
hex
hex関数は、数値を16進数で表示します。
data = 12 print(hex(data)) #=> 0xc
ファイル入力
バイナリファイルのデータを読みだす場合、readメソッドが使用できます。 しかし、readメソッドは読み込んだデータを文字列として扱うので、整数値として扱う場合は関数ordを使用します。
バイナリファイル "tmp.bin" の中身が16進数表示で 08 09 0A 0B 0C FF の場合
f = open('tmp.bin', 'rb') while True: d = f.read(1) if len(d) == 0: break print ord(d), print '(%x) ' % ord(d), f.closeこの実行結果は以下になります。
8 (8) 9 (9) 10 (a) 11 (b) 12 (c) 255 (ff)
Python3の場合は、以下のようにします。
with open('tmp.bin', 'rb') as f: while True: d = f.read(1) if len(d) == 0: break print('%s (%x) ' % (ord(d), ord(d)), end='')
ファイル出力
バイナリデータをファイルに出力する方法
バイト単位の出力
Python2では、ファイルにバイト単位でバイナリデータを書き込みたい場合, chr()を使用する方法があります。
data = [8, 9, 10, 11, 12, 255] f = open('tmp.bin', 'wb') for d in data: f.write(chr(d)) # 08 09 0a 0b 0c ff f.closeこれを実行すると、ファイルサイズが5バイトのtmp.binができます。
Python3では、整数型に追加されたto_byteメソッドを使います。
data = [8, 9, 10, 11, 12, 255] with open('tmp.bin', 'wb') as f: for d in data: f.write(d.to_bytes(1, byteorder='little')) # 08 09 0a 0b 0c ff・整数型における追加のメソッド・to_bytes
to_byteメソッドの第二引数は、バイトオーダーの文字列"big"、"little"、を指定します。 バイトオーダーが分からない場合は、現システムのバイトオーダーの文字列が入ったsys.byteorderを指定する方法があります。
import sys
data = [8, 9, 10, 11, 12, 255]
with open('tmp.bin', 'wb') as f:
for d in data:
f.write(d.to_bytes(1, byteorder=sys.byteorder)) # 08 09 0a 0b 0c ff
第一引数のバイト数が1の場合、バイトオーダーは"big"、または"little"のどちらを指定しても結果は変わりません。第一引数が2以上の場合は、バイトオーダーを正しく指定する必要があります。 以下はバイト数に2、バイトオーダーに"little"を指定した例です。
data = [8, 9, 10, 11, 12, 255] with open('tmp.bin', 'wb') as f: for d in data: f.write(d.to_bytes(2, byteorder='little'))
$ hexdump -C tmp.bin 00000000 08 00 09 00 0a 00 0b 00 0c 00 ff 00 |............| 0000000csys.byteorder = "little"の場合、第一引数のバイト数を2とすると、上記のようにリトルエンディアンになります。(0x0008 => 0x08 0x00)
bytearrayを使ったバイト単位の出力
bytearrayを使うことでも、バイト単位でファイルに書き込むことができます。
data = bytearray([0x12, 0x34, 0xac, 0xff]) f = open('tmp.bin', 'wb') f.write(data) f.close()上記を実行すると、 tmp.binというファイルが作成され、その中身は16進数で、以下のように 12 34 AC FF になります。
$ hexdump -v -C tmp.bin
00000000 12 34 ac ff |.4..|
00000004
bytearray.append
bytearrayは、appendメソッドでデータの追加も可能です。
data = range(15, 15+16*6, 16) #=> 0x0F(15) 0x1F(15+16) ... 0x5F(15+16*5) bt = bytearray([0, 1, 2]) f = open('tmp.bin', 'wb') for d in data: bt.append(d) f.write(bt) f.close()上記の実行で作成されるファイルの中身は 00 01 02 のあとに、0F 1F 2F 3F 4F 5F が続きます。
$ hexdump -v -C a.bin 00000000 00 01 02 0f 1f 2f 3f 4f 5f |...../?O_| 00000009
指定位置のデータだけ上書き
seekでファイルオブジェクトの位置を変更できます。
seek(offset, from_what)第二引数のfrom_whatは参照点になり、0がファイルの先頭、1が現在のファイル位置、2がファイルの終端になります。デフォルトは0で、ファイルの先頭になります。 第一引数のoffsetは、参照点からの位置になり、from_what = 0の場合、offset = 0がファイルの先頭1バイト目の位置になります。
Python3の場合、以下のようになります。
import sys with open('tmp.bin', 'wb') as f: f.seek(3) f.write(0x80.to_bytes(1, byteorder=sys.byteorder))上記では、ファイルの先頭を1バイト目とすると、4バイト目を0x80に書き換えます。
hexdump -C tmp.bin
00000000 08 09 0a 80 0c ff |......|
00000006