1. 画面表示
  2. ファイル入力
    • ファイル出力

    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))      #=>   66
      ord関数と、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              |............|
        0000000c
        sys.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