
先日簡単にバイナリファイルを読み込む事が出来る記事を読んだんだけど、いつになったらデータをバイナリファイルへ書き込む記事が出てくるんだろう?

なかなか記事の更新が出来ずにスミマセン。
pythonでバイナリファイルへの書き込みを行う方法は色々ありますが、この記事では、最も簡単な1バイトずつデータを書き込んでいく方法をご紹介します。
また記事の最後では、2バイト以上書き込む場合に必要となるバイトオーダーについても少しだけ解説します。
OS : Windows10(64bit版)
Python version : 3.7.3
Pythonで簡単にバイナリファイルへデータを書き込む方法
先日の記事でご紹介した、バイナリファイルを生成するコードを用いて解説したいと思います。
f = open(r"C:\Python_source\02_BinaryFile\SampleBinary.bin","wb")
for i in range(32):
f.write(i.to_bytes(1,"big"))
f.close()
ファイルをバイナリモードで開く
1行目ではファイルをバイナリモード(第二引数に”wb”を入力)で開いています。
ディレクトリのパスは必要に応じて変更してください。ディレクトリが存在すれば、勝手にファイルが作られます。ファイルを開く時のモードについては、次の記事が参考になるかなと思います。
range()関数
2行目は本サイトで何度もしているfor文とrange()関数のセット記述となりますが、これでfor文のブロックに記述された処理を32回まわせます。この時変数iには0から31までの整数が+1のステップで代入され、forのブロック内で使用する事が可能です。変数iは使わなくても問題ありません。また変数iを3からスタートしたいなぁという場合や、増加(減少)させる数値を変更したい場合は、range()関数へ与える引数を変える事で簡単に変更できます。具体的には、
range(はじめの数, 終わりの数, 増加する数)
です。
引数を2つにした場合は
range(はじめの数, 終わりの数)
となります。
引数が1つの場合は
range(終わりの数)
です。この時、はじめの数は0, 増加する数は1となります。
あと終わりの数というのは、正確には終わる1つ前の数となります。インタラクティブモードで動作を確認してみましょう。
>>> for i in range(32,25,-1):
print(i)
32
31
30
29
28
27
26 # -1ステップなので25まではいかない。
for i in range(11,17,2):
print(i)
11
13
15 # 2ステップなので15で終わる。
# 17までは行かない。
バイナリファイルへのアクセスは、リードもライトもbytes型
バイナリファイルをリードした時、リードしたデータを代入した変数はbytes型でした。バイナリファイルへデータをライトする時も同様に、書き込みたいデータ(文字や数値)をbytes型へ変換してライトする必要があります。
文字列をbytes型へ変換する
文字列をbytes型へ変換するのは超簡単です。Pythonでは文字列を表す時に、その対象を “ または ‘ で括れば良いですが、その頭にbを付けるだけでOKです。実際に確認してみましょう。
>>> mozi = "mogumogu"
>>> type(mozi)
<class 'str'> # 文字列型
>>> mozi_byte = b"mogumogu"
>>> type(mozi_byte)
<class 'bytes'> # bytes型になってる。
後はバイナリモードで開いたファイルオブジェクトに
f.write(mozi_byte)
とするだけで、文字列をバイナリファイルへ書き込む事が出来ます。
バイナリエディタで確認すると、”6D 6F 67 75 6D 6F 67 75”(mogumoguのASCIIコード)が並んでいる事が分かります。
整数をbytes型へ変換する
ソースコードの3行目に示されている
f.write(i.to_bytes(1,”big”))
で整数のbytes型への変換が行われています。細かく分解して見ていきましょう。
>>> i=10
>>> byte_data = i.to_bytes(1,"big")
>>> type(byte_data)
<class 'bytes'>
to_bytes()は整数を操作するメソッドで、その名の通り、整数をbytesオブジェクトへ変換します。メソッドの引数としては
“整数”.to_bytes(“バイト数”, “バイトオーダー”, “符号つき”)
となります。マイナスの整数を扱う場合は、引数に明記する必要があります。
(-10).to_bytes(1,"big",signed=True)
b'\xf6'
>>> (-10).to_bytes(1,signed=True,byteorder='little')
b'\xf6'
”整数”は、数値を直接与える場合は例のように()で括る必要があります。変数の場合は()はあってもなくても良いです。
“バイト数” には1以上の数値を設定します。0でもエラーとはならないですが、空のバイトデータを返されます。1バイトは8ビットの1データなので、256段階のデータを扱う事が出来ます。具体的には符号無しであれば0〜256の整数、符号付きの場合は−128から127の整数を扱う事が出来ます。もし扱う整数がこの範囲を超えてしまう場合は、バイト数を増やす必要があります。また1バイトに収まるデータを2バイト以上で書き込む事も出来ますが、無駄にファイルサイズを大きくするだけなので、何か特別な理由でもない限りはやめておいた方が良いでしょう。
“バイトオーダー”はその名の通り、バイトデータの並びです。”big”か’little’で与えます。引数名(byteorder=)を省略する場合は、例のように必ず第二引数として与えます。1バイトデータの場合は上記例のように結果は変わりません。では4バイトデータの場合で見てみましょう。
>>> word = (-2047383243).to_bytes(4,"big",signed=True)
>>> print ("%x,%x,%x,%x" % (word[0],word[1],word[2],word[3]))
85,f7,69,35
>>> word = (-2047383243).to_bytes(4,"little",signed=True)
>>> print ("%x,%x,%x,%x" % (word[0],word[1],word[2],word[3]))
35,69,f7,85 #バイトデータの並びが逆になってる!
2バイト以上のデータでは、バイトデータの並びが逆になっている事が確認出来ます。ここは扱うバイナリデータのフォーマットで選択する事になるかと思います。もし社内独自フォーマットを決める立場にあれば、人間に優しい”big”エンディアンをお勧めします。ただwindowsやMacにLinuxと、多くOSはハードに優しい”little”エンディアンを採用しているので、バイナリデータを扱っているとバイトオーダーを意識させられる事はあるでしょう。
それでは本日はこの辺にしたいと思います。最後まで読んで頂き、ありがとうございました。