【Python】バイナリファイルのランダムアクセスの基本・後編【入門第73回】
目次
バイナリファイルへのランダムアクセス・後編
前回↓の続きです。
引き続きバイナリファイルへのランダムアクセスについて解説していきたいと思います。
今回も全般的にseek
メソッドを使いますが、前回より少し難易度が上がってると思います。
初心者の方はじっくり取り組んでみてください。
今回は具体的には↓を見ていきます。
ファイルの任意の位置にシークする
任意の位置のデータを読み込む
任意の位置のデータを書き換える
ほかのファイル入出力についての入門記事は↓をご覧ください。
- 【Python】ファイルを開いて閉じる。open関数の使い方【入門第54回】
- 【Python】テキストファイルの読み込み(モードr, rt)【入門第55回】
- 【Python】テキストファイルへの書き込み(モードw, wt)【入門第56回】
- 【Python】テキストファイルへの追記(モードa, at)【入門第57回】
- 【Python】テキストファイルを排他的に開いて書き込む(モードx, xt)【入門第58回】
- 【Python】バイナリファイルの読み込み(モードrb)【入門第59回】
- 【Python】バイナリファイルへの書き込み(モードwb)【入門第60回】
- 【Python】バイナリファイルへの追記(モードab)【入門第61回】
- 【Python】バイナリファイルを排他的に開いて書き込む(モードxb)【入門第62回】
- 【Python】テキストファイルを読み書きモードで開く(モードr+, r+t)【入門第63回】
- 【Python】テキストファイルを読み書きモードで新規作成して開く(モードw+, w+t)【入門第64回】
- 【Python】テキストファイルを読み書きモードで、新規作成して、排他的に開く(モードx+, x+t)【入門第65回】
- 【Python】バイナリファイルを読み書きモードで開く(モードr+b)【入門第66回】
- 【Python】バイナリファイルを読み書きモードで新規作成して開く(モードw+b)【入門第67回】
- 【Python】バイナリファイルを読み書きモードで排他的に開く(モードx+b)【入門第68回】
- 【Python】テキストファイルを読み書きモードで追記する(モードa+)【入門第69回】
- 【Python】バイナリファイルを読み書きモードで追記する(モードa+b)【入門第70回】
- 【Python】バイナリファイルのランダムアクセスの基本・前編【入門第71回】
- 【Python】バイナリファイルのランダムアクセスの基本・後編【入門第72回】
- 【Python】ランダムアクセスによる固定長レコードの読み書き・前編【入門第73回】
ファイルの任意の位置にシークする
たとえば↓のような内容のalphas.dat
ファイルがあるとします。
:::text
abcdefghijklmn
このファイルの「ファイル先頭から3
バイト目」にシークしたい場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
fp.seek(3, os.SEEK_SET)
seek
メソッドの第2引数にファイル先頭を表す定数SEEK_SET
を、第1引数にはその基点からのバイト数を指定します。
「ファイル末尾から-3
バイト目」にシークしたい場合は↓のようにします。
:::python
with open('alphas.dat', mode='r+b') as fp:
fp.seek(-3, os.SEEK_END)
おなじようにseek
メソッドの第2引数にファイル末尾を表す定数SEEK_END
を、第1引数にはその基点からのバイト数を指定します。
さらにSEEK_CUR
定数を使うと相対位置からの指定ができます。
たとえばいったんファイル先頭にオフセット位置を移動して、その位置から3
バイト進みたい場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイル先頭にオフセット位置をシーク
fp.seek(0, os.SEEK_SET)
# 現在の位置(ファイル先頭)から3バイト進める
fp.seek(3, os.SEEK_CUR)
さらにその位置から5
バイト進みたい場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイル先頭にオフセット位置をシーク
fp.seek(0, os.SEEK_SET)
# 現在の位置から3バイト進める
fp.seek(3, os.SEEK_CUR)
# 現在の位置から5バイト進める
fp.seek(5, os.SEEK_CUR)
さらにさらにその位置から2
バイト戻りたい場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイル先頭にオフセット位置をシーク
fp.seek(0, os.SEEK_SET)
# 現在の位置から3バイト進める
fp.seek(3, os.SEEK_CUR)
# 現在の位置から5バイト進める
fp.seek(5, os.SEEK_CUR)
# 現在の位置から2バイト戻る
fp.seek(-2, os.SEEK_CUR)
最後に↑の処理でseek
した直後にtell
メソッドを使って現在のオフセット位置を確認してみましょう。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイル先頭にオフセット位置をシーク
fp.seek(0, os.SEEK_SET)
print(fp.tell())
# 現在の位置から3バイト進める
fp.seek(3, os.SEEK_CUR)
print(fp.tell())
# 現在の位置から5バイト進める
fp.seek(5, os.SEEK_CUR)
print(fp.tell())
# 現在の位置から2バイト戻る
fp.seek(-2, os.SEEK_CUR)
print(fp.tell())
↑のコードの実行結果は↓のようになります。
:::text
0
3
8
6
最初にSEEK_SET
でオフセット位置をファイル先頭にしているので、tell
の返す値は0
になります。
そこから3
バイト進むのでオフセット位置は3
バイト目に、そこから5
バイト進むのでオフセット位置は8
バイト目に、最後にそこから2
バイト戻ってオフセット位置が6
になります。
やってることは足し算と引き算に近いですね。
しかし直観的にはなかなかわかりづらいと思います。
SEEK_CUR
はランダムアクセスを複雑にしている定数でもあるし、出来ることを広げてくれるメソッドでもあります。
この暴れ馬を操るにはある程度、練習が必要だと言えるでしょう。
任意の位置のデータを読み込む
↓のような内容のalphas.dat
ファイルがあります。
:::text
abcdefghijklmn
このファイルの「ファイル先頭3
バイト目からデータを読み込みたい」場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイルの先頭から3バイト目にシーク
fp.seek(3, os.SEEK_SET)
# 現在位置からデータをファイル末尾まで読み込む
data = fp.read()
print(data)
↑のコードの実行結果は↓のようになります。
:::text
b'defghijklmn'
read
メソッドは第1引数に読み込むバイト数を指定することが出来ます。
これを使い、たとえば「ファイル先頭6
バイト目からデータを3
バイト読み込む」場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイルの先頭から3バイト目にシーク
fp.seek(6, os.SEEK_SET)
# 現在位置からデータを3バイト読み込む
data = fp.read(3)
print(data)
↑のコードの実行結果は↓のようになります。
:::text
b'ghi'
このように、seek
メソッドとread
メソッドを組み合わせると、ファイルの任意の位置のデータを切り取ることが出来ます。
このテクニックは固定長レコードの取り扱いでもよく使います。
- [TODO: リンク(ランダムアクセス、固定長レコード)]
任意の位置のデータを書き換える
↓のような内容のalphas.dat
ファイルがあります。
:::text
abcdefghijklmn
このファイルの「ファイル先頭3
バイト目からデータを書き込みたい」場合は↓のようにします。
:::python
import os
with open('alphas.dat', mode='r+b') as fp:
# ファイルの先頭から3バイト目にシーク
fp.seek(3, os.SEEK_SET)
# 3バイト目以降のデータを上書き
fp.write(b'DEF')
↑のコードを実行するとalphas.dat
の中身は↓のようになります。
:::text
abcDEFghijklmn
write
はファイルのオフセット位置がファイル末尾ならデータをファイルに追記してファイルの容量を増やしますが、↑のように任意のオフセット位置から書き込む場合は、↑のように書きこんだバイト数分、既存のデータを上書きします。
おわりに
以上でバイナリファイルのランダムアクセスの基礎編はおわりです。
おつかれさまでした。
次回は基礎からの応用として、固定長レコードの読み書きをやりたいと思います。
以上、次回に続きます。
また見てね