[解決!Python]バイナリファイルを読み書きするには:文字列と整数編:解決!Python
バイナリファイルを読み書きする基本と、文字列および整数をバイナリファイルに書き込む方法を紹介する。
# 文字列のバイナリファイルへの書き込み
with open('test.bin', 'wb') as f:
s = 'ディープインサイダー'
b = s.encode() # 文字列もバイト列にエンコードする必要がある
f.write(b) # バイナリファイルにはバイト列しか渡せない
# 文字列のバイナリファイルからの読み込み
with open('test.bin', 'rb') as f:
b = f.read()
s = b.decode()
print(s) # ディープインサイダー
# 整数のバイナリファイルへの書き込みと読み込み
from sys import byteorder
print(byteorder) # littleもしくはbig
length = 4 # 4バイト長整数に変換する
with open('test.bin', 'wb') as f:
n = 123456
b = n.to_bytes(length, byteorder) # nを4バイト長の符号なし整数に変換
f.write(b)
# 整数のバイナリファイルからの読み込み
with open('test.bin', 'rb') as f:
b = f.read(length) # lengthだけバイナリファイルから読み込み
n = int.from_bytes(b, byteorder) # バイト列を整数に変換する
print(f'read data: {n}') # read data: 123456
# 整数リストのバイナリファイルへの書き込みと読み込み
nlist = [1, 2, 3, 4, 5, 6]
with open('test.bin', 'wb') as f:
# 整数リストをバイト列リストに変換してファイルに書き込み
blist = [n.to_bytes(length, byteorder) for n in nlist]
f.writelines(blist)
# 整数リストをバイト列に変換してファイルに書き込み
#b = b''.join([n.to_bytes(length, byteorder) for n in nlist])
#f.write(b)
# バイナリファイルからの整数の連続読み込み
with open('test.bin', 'rb') as f:
result = []
b = f.read(length) # lengthだけバイナリファイルから読み込み
while b: # データがあるだけ以下を繰り返す
n = int.from_bytes(b, byteorder) # 読み込んだデータを整数に変換
result.append(n) # リストに追加
b = f.read(length) # lengthだけバイナリファイルから読み込み
print(result) # [1, 2, 3, 4, 5, 6]
# バイト列を読み込んだ後にmemoryviewオブジェクトを得て、型変換を行う
with open('test.bin', 'rb') as f:
b = f.read()
mv = memoryview(b)
result = mv.cast('i').tolist()
print(result) # [1, 2, 3, 4, 5, 6]
バイナリファイルを読み書きする基本
バイナリファイルを読み書きする際には、open関数のmode引数に'b'を付加する(読み込みなら'rb'、書き込みなら'wb')。
# バイナリファイルを読み込みモードでオープンする
with open('filename', 'rb') as f:
pass # ファイルを利用する
# バイナリファイルを書き込みモードでオープンする
with open('filename', 'wb') as f:
pass # ファイルを利用する
オープンしたファイルに対しては、テキストファイルと同様に、readメソッドやwriteメソッドなどを呼び出せる。
バイナリファイルを読み込み用にオープンしたときには、以下のように反復可能オブジェクトとしてfor文にファイルを渡すことも可能だ。
with open('filename', 'rb') as f:
for data in f: # 行末(\n)またはEOFまでを読み込んで以下を繰り返す
pass # 何かの処理を行う
上のような形でバイナリファイルから読み込むときには改行文字は常に「\n」となる点は覚えておこう。
特定のバイト数だけを読み込むのであれば、readメソッドに読み込みたいバイト数を指定する。
with open('filename', 'rb') as f:
size = 32
data = f.read(size)
while data:
# 何かの処理を行う
data = f.read(size)
バイナリファイルの読み込みを行うと、得られるのは「バイト列」(bytes型のオブジェクト)になっている点には注意しよう。それらは何らかの形で特定の型のオブジェクトに変換する必要がある。
バイナリファイルを書き込み用にオープンしたときには、元のデータを「バイト列」に変換したものをwriteメソッドなどで書き込んでいく。
with open('filename', 'wb') as f:
s = 'hello'
b = s.encode() # 文字列をバイト列に変換
f.write(b) # 変換後のバイト列をバイナリファイルに書き込み
以下では、バイナリファイルへの書き込みの例として、文字列と整数を取り上げる。これら以外のオブジェクトは次回に説明をするstructモジュールを使用することでバイナリファイルへの書き込みが比較的簡単に行えるようになる。
バイナリファイルに対して文字列を読み書きする
文字列にはバイト列への変換を行うencodeメソッドがあり、バイト列には文字列への変換を行うdecodeメソッドがある。
バイナリファイルに文字列を書き込む例を以下に示す。
with open('test.bin', 'wb') as f:
s = 'ディープインサイダー'
b = s.encode() # 文字列もバイト列にエンコードする必要がある
f.write(b)
この例では、バイナリファイルを書き込みモードでオープンした後、文字列sのencodeメソッドを呼び出してバイト列にエンコードして、それをwriteメソッドで書き出している。なお、encodeメソッドのencoding引数にエンコーディングを指定することも可能だ。
次にバイナリファイルの読み込みの例を示す。
with open('test.bin', 'rb') as f:
b = f.read()
s = b.decode()
print(s) # ディープインサイダー
このコードでは、バイナリファイルを読み込み用にオープンして、readメソッドで全ての内容を読み出して、取得したバイト列をdecodeメソッドで文字列にデコードしている。文字列のencodeメソッドと同様に、バイト列のdecodeメソッドでもエンコーディングを指定できる。
バイナリファイルに対して整数を読み書きする
文字列とバイト列の変換には文字列のencodeメソッド/バイト列のdecodeメソッドを使用したが、整数とバイト列の変換には整数型(int型)のto_bytesインスタンスメソッドとfrom_bytesクラスメソッドを使用する。
to_bytesメソッドの構文は以下の通り。
int.to_bytes(length, byteorder, signed=False)
length引数とbyteorder引数は指定が必須で、signed引数は省略可能なキーワード専用引数である。length引数は整数を何バイトのバイト列に変換するかを指定する。byteorder引数はバイトオーダーの指定で、signed引数は2の補数を使用するかどうかの指定となる(負数をバイト列に変換する場合には「signed=True」のようにキーワードを明示して指定)。
整数をバイト列に変換する例を以下に示す。
from sys import byteorder # プログラムを実行するPCのバイトオーダー
print(byteorder) # littleもしくはbig
length = 4 # 4バイト長整数に変換する
n = 123456
b = n.to_bytes(length, byteorder)
print(b) # b'@\xe2\x01\x00'もしくはb'\x00\x01\xe2@'
バイト列を整数に変換するのに使用するint.from_bytesメソッドの構文は次の通り。
int.from_bytes(bytes, byteorder, signed=False)
bytes引数には整数へ変換したいバイト列を渡す。byteorder/signed引数はto_bytesメソッドと同じである。
上で変換したバイト列を整数に変換する例を以下に示す。
v = int.from_bytes(b, byteorder)
print(v) # 123456
以上を踏まえて、整数のバイト列の変換とバイナリファイルへの書き込みの例を以下に示す。
from sys import byteorder
print(byteorder)
length = 4
with open('test.bin', 'wb') as f:
n = 123456
b = n.to_bytes(length, byteorder)
f.write(b)
ここでは、バイナリファイルを書き込みモードでオープンし、上で見た手順で整数をバイト列に変換し、それをwriteメソッドで書き込んでいる。
バイト列に変換された値が書き込まれているバイナリファイルから、値を読み込む例を以下に示す。
with open('test.bin', 'rb') as f:
b = f.read(length) # lengthだけバイナリファイルから読み込み
n = int.from_bytes(b, byteorder) # バイト列を整数に変換する
print(f'read data: {n}') # read data: 123456
重要なのは、書き込みを行った上のコードでは、整数値を4バイト長の符号なし整数に変換していたところだ。そのため、このコードではバイナリファイルから4バイトだけデータを読み込むようにreadメソッドに読み込むバイト数をしている。読み込んだ後は、先ほど見たようにint.from_bytesメソッドを使って整数値に変換するだけだ。
バイナリファイルに対して、複数の整数値を書き込む
バイナリファイルに整数を1つだけ書き込むのではなく、複数の整数値を書き込むには整数値を要素とするリスト(またはタプルなど)をバイト列のリストに変換して、それをwritelinesメソッドで書き込むのが簡単だろう。
以下に例を示す。
nlist = [1, 2, 3, 4, 5, 6]
with open('test.bin', 'wb') as f:
blist = [n.to_bytes(length, byteorder) for n in nlist]
f.writelines(blist)
#b = b''.join([n.to_bytes(length, byteorder) for n in nlist])
#f.write(b)
ここでは、整数値を要素とするリストを基に、上で見たto_bytesメソッドを使用して各要素をバイト列に変換したバイト列リストを用意している。そして、それをwritelinesメソッドで一気にファイルに書き込んでいる。ただし、コメントアウトされている行のように、バイト列のjoinメソッドを使って、バイト列のリストを1つのバイト列に連結し、それをwriteメソッドで書き込む方法もある。
バイナリファイルから複数の整数値を読み込む例を以下に示す。
with open('test.bin', 'rb') as f:
result = []
b = f.read(length)
while b:
n = int.from_bytes(b, byteorder)
result.append(n)
b = f.read(length)
print(result) # [1, 2, 3, 4, 5, 6]
この例では、上で見たようにreadメソッド呼び出し時に一度に読み込むサイズを指定して、バイナリファイルからバイト列を整数1個分ずつ読み込んで、それをint.from_bytesメソッドで整数に変換したものをリストに追加している。
データを1つずつ読み込むのではなく、一度に全てのデータを読み込んでから、それをmemoryviewオブジェクトのcastメソッドとtolistメソッドを使って以下のようにしてもよいだろう(詳細な解説は省略する)。
with open('test.bin', 'rb') as f:
b = f.read()
mv = memoryview(b)
result = mv.cast('i').tolist()
print(result)
文字列だけ、あるいは整数値だけをバイナリファイルに書き込むには今見た方法でも十分だが、文字列や整数以外の型のデータをバイナリファイルに対して読み書きしたり、一定の構造を持ったデータをバイナリファイルに対して読み書きしたりするには次回紹介するstructモジュールを使用するのがよい。
Copyright© Digital Advantage Corp. All Rights Reserved.