【Python】バイナリファイルを読み書きする

python

Pythonでバイナリファイルを読み書きする必要が生じたので、そのためのメモです。

ファイルのオープン・クローズ

ファイルをオープンするにはopen()をつかいます。この時バイナリファイルであることを示すため、"rb"はたは"wb", "ab", "xb"を指定します。一方、クローズするにはclose()を使います。

f = open("test.dat", "wb")
f.close()

"rb"等の違いは以下のようになります。

オプション意味
"rb"読み込み
"wb"書き込み
"ab"書き込み+ファイルの末尾に移動
"xb"ファイルが存在しない時に書き込み。存在する場合はエラー

読み書きの操作が連続し、操作が終了したらファイルをクローズするような場合はwith構文を使うと便利です。ブロックが終了すると自動的にファイルがクローズされます。

with open("test.dat", "rb") as f:
    a = f.read()

ランダムアクセス

現在位置を返す

ファイルの現在位置を返すにはtell()を使います。

# fはファイルオブジェクト
pos = f.tell()

ポインタの移動

ファイルのポインタを移動するにはseek()を使います。seek()にはファイルのどこを基準とするかを示すオプションが設定できます。値はosモジュールで定義されています。省略するとos.SEEK_SETが指定されます。

基準となる位置指定する値
os.SEEK_SETファイルの先頭
os.SEEK_CUR現在のポインタ正または負
os.SEEK_ENDファイルの終わり
# osモジュールをインポートする
import os

# fはファイルオブジェクト
f.seek( 5, os.SEEK_SET)  # 先頭から5バイト目
f.seek(-3, os.SEEK_CUR)  # 現在位置から3バイト前
f.seek(-5, os.SEEK_END)  # ファイル終端から5バイト前

読み書き

ファイルへのバイト列の読み込みにはread()を、書き込みにはwrite()を使います。文字列や数値を読み書きするには、バイト列を経由する必要があり、変換が必要です。

# fはファイルオブジェクト
b = f.read(32)  # 32バイト読み込み

b = b'123'
f.write(b)      # バイト列書き込み

文字列

文字列とバイト列の変換には、decode()encode()を使います。また、後述するstructモジュールも使用できます。

# fはファイルオブジェクト
b = f.read(10)
str = b.decode() # バイト列を文字列に変換

str = "12345"
b = str.encode() # 文字列をバイト列に変換
f.write(b)

数値の読み書き

整数の読み書き

整数とバイト列の変換には、int.from_byte()to_bytes()を使います。また、後述するstructモジュールも使用できます。

# byteorderを使うために必要
from sys import byteorder

# fはファイルオブジェクト
b = f.read(4)
val = int.from_bytes(b, byteorder) # 4バイトのバイト列を整数に変換

val = 12345
b = val.to_bytes(4, byteorder)     # 整数を4バイトのバイト列に変換
f.write(b)

浮動小数点数の読み書き

浮動小数点数とバイト列の変換にはstructモジュールを使用します。unpack()の戻り値はタプルなので、受け取り方に注意が必要です。

# structモジュールを使うために必要
from struct import unpack, pack

# fはファイルオブジェクト
b = f.read(8)
val, = unpack('d', b) # 8バイトのバイト列を浮動小数点数に変換

val = 12.345
b = pack('f', val)    # 浮動小数点数を4バイトのバイト列に変換
f.write(b)

unpack()およびpack()で指定可能な書式指定文字には、以下のものがあります。

記号意味
'f'単精度浮動小数点数(4バイト)
'd'倍精度浮動小数点数(8バイト)
'q'符号付き整数(8バイト)
'Q'符号なし整数(8バイト)
'i', 'l'符号付き整数(4バイト)
'I', 'L'符号なし整数(4バイト)
'h'符号付き整数(2バイト)
'H'符号なし整数(2バイト)
'c'文字(1バイトのバイト列)
'b'符号付き整数(1バイト)
'B'符号なし整数(1バイト)
's', 'p'固定長バイト列(10sなどのように長さと一緒に指定)

複数のデータの読み書き

複数のデータを纏めて読み書きするには、structモジュールを使用します。

# structモジュールを使うために必要
from struct import unpack, pack, calcsize

format = "15sl10s"
size = calcsize(format) # フォーマットからバッファのサイズを計算する

b = f.read(size)
s1, val, s2 = unpack(format, b)
s1 = s1.strip(b'\0x00').decode() # ヌル文字を取り除く
s2 = s2.strip(b'\0x00').decode() # ヌル文字を取り除く

s1 = "テスト"
val = 123
s2 = "abcdefghij"
b = pack(format, s1.encode(), val, s2.encode())
f.write(b)

以上、Pythonでのバイナリファイルの読み書き方法についてまとめてみました。

python

Posted by izadori