【Python】バイナリファイルのランダムアクセスの基本・後編【入門第73回】

210, 2019-10-03

目次

バイナリファイルへのランダムアクセス・後編

前回↓の続きです。

引き続きバイナリファイルへのランダムアクセスについて解説していきたいと思います。
今回も全般的にseekメソッドを使いますが、前回より少し難易度が上がってると思います。
初心者の方はじっくり取り組んでみてください。

今回は具体的には↓を見ていきます。

  • ファイルの任意の位置にシークする

  • 任意の位置のデータを読み込む

  • 任意の位置のデータを書き換える

ほかのファイル入出力についての入門記事は↓をご覧ください。

ファイルの任意の位置にシークする

たとえば↓のような内容のalphas.datファイルがあるとします。

abcdefghijklmn

このファイルの「ファイル先頭から3バイト目」にシークしたい場合は↓のようにします。

import os

with open('alphas.dat', mode='r+b') as fp:
    fp.seek(3, os.SEEK_SET)

seekメソッドの第2引数にファイル先頭を表す定数SEEK_SETを、第1引数にはその基点からのバイト数を指定します。

「ファイル末尾から-3バイト目」にシークしたい場合は↓のようにします。

with open('alphas.dat', mode='r+b') as fp:
    fp.seek(-3, os.SEEK_END)

おなじようにseekメソッドの第2引数にファイル末尾を表す定数SEEK_ENDを、第1引数にはその基点からのバイト数を指定します。

さらにSEEK_CUR定数を使うと相対位置からの指定ができます。
たとえばいったんファイル先頭にオフセット位置を移動して、その位置から3バイト進みたい場合は↓のようにします。

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バイト進みたい場合は↓のようにします。

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バイト戻りたい場合は↓のようにします。

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メソッドを使って現在のオフセット位置を確認してみましょう。

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())

↑のコードの実行結果は↓のようになります。

0
3
8
6

最初にSEEK_SETでオフセット位置をファイル先頭にしているので、tellの返す値は0になります。
そこから3バイト進むのでオフセット位置は3バイト目に、そこから5バイト進むのでオフセット位置は8バイト目に、最後にそこから2バイト戻ってオフセット位置が6になります。

やってることは足し算と引き算に近いですね。
しかし直観的にはなかなかわかりづらいと思います。

SEEK_CURはランダムアクセスを複雑にしている定数でもあるし、出来ることを広げてくれるメソッドでもあります。
この暴れ馬を操るにはある程度、練習が必要だと言えるでしょう。

任意の位置のデータを読み込む

↓のような内容のalphas.datファイルがあります。

abcdefghijklmn

このファイルの「ファイル先頭3バイト目からデータを読み込みたい」場合は↓のようにします。

import os

with open('alphas.dat', mode='r+b') as fp:
    # ファイルの先頭から3バイト目にシーク
    fp.seek(3, os.SEEK_SET)

    # 現在位置からデータをファイル末尾まで読み込む
    data = fp.read()
    print(data)

↑のコードの実行結果は↓のようになります。

b'defghijklmn'

readメソッドは第1引数に読み込むバイト数を指定することが出来ます。
これを使い、たとえば「ファイル先頭6バイト目からデータを3バイト読み込む」場合は↓のようにします。

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)

↑のコードの実行結果は↓のようになります。

b'ghi'

このように、seekメソッドとreadメソッドを組み合わせると、ファイルの任意の位置のデータを切り取ることが出来ます。

このテクニックは固定長レコードの取り扱いでもよく使います。

  • [TODO: リンク(ランダムアクセス、固定長レコード)]

任意の位置のデータを書き換える

↓のような内容のalphas.datファイルがあります。

abcdefghijklmn

このファイルの「ファイル先頭3バイト目からデータを書き込みたい」場合は↓のようにします。

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の中身は↓のようになります。

abcDEFghijklmn

writeはファイルのオフセット位置がファイル末尾ならデータをファイルに追記してファイルの容量を増やしますが、↑のように任意のオフセット位置から書き込む場合は、↑のように書きこんだバイト数分、既存のデータを上書きします。

おわりに

以上でバイナリファイルのランダムアクセスの基礎編はおわりです。
おつかれさまでした。
次回は基礎からの応用として、固定長レコードの読み書きをやりたいと思います。

以上、次回に続きます。

また見てね

関連動画





スポンサーリンク

スポンサーリンク

スポンサーリンク

スポンサーリンク