file I/O, with 構文

ファイルの使い方は、詳しくはpython の公式文書を参照してください。

今回は、ファイル読み書きをするプログラム例を用いて簡単な事例説明をおこないます。

バイトで読書き

ファイルはバイト列なので、まずはバイト列のまま処理してみます。 ここではファイルコピーをおこなうreadwrite_file_in_bytes()を説明しますね。

まず、ファイルを開いて、bytes 型の data を取得するのはこんな感じでOK.

with open(filename, 'rb') as file_in:
    while True:
        data: bytes = file_in.read(BUF_SIZE)
        if not data:  # ファイルを読み終わったら終了
            break
        print(data)  # bytes を表示してみる
  • with open でファイルを開く
    • rb は、r がファイルの read を意味し、b は binary mode を意味する
    • 開いたファイルは file_in でアクセス可能
    • 後述するように、with ブロックから抜けた際、ファイルを閉じてくれる
  • file_in.read(BUF_SIZE) で、指定サイズのデータを取得

一方で、ファイルへの書込みはこんな感じの操作になります。

with open(out_filename, 'wb') as file_out:
    # loop
        file_out.write(data)
  • with open だけど、今度は wb で書込み+binary モード
  • file_out.write(data) で、data を書込み

なので、file copy だと、こんな感じ。

def readwrite_file_in_bytes(filename, out_filename):
    # ファイルを開いて
    with open(filename, 'rb') as file_in:
        with open(out_filename, 'wb') as file_out:
            print('Start: readwrite_file_in_bytes')
            while True:
                # ファイルの中身を
                data: bytes = file_in.read(BUF_SIZE)
                if not data:  # ファイルを読み終わったら終了
                    print('Done: readwrite_file_in_bytes')
                    break
                print(data)
                file_out.write(data)

テキストを1行ずつ読書き

今度は、テキストファイルを読書きしてみましょう。 いくつかやり方があるのですが、素直に1行ずつ読書きする例(readwrite_file_in_lines()だけ、説明しておきます。

ファイルを読む部分はこれぐらい。

with open(filename, 'r', encoding='utf-8') as file_in:
    for line in file_in:   # line は改行含む
        print(line)        # なので、改行x2 出力される
  • with open でファイルを開く
    • 今度は text mode で読むので rb じゃなく r モード
    • テキストファイルを開く際、文字コード指定をおこなう (encoding='utf-8')
      • python の関数呼び出し引数に関する解説は、こちら
  • for line in file_in: で、file_inから1行ずつデータを取得してループ実行
    • 取得したデータには line 変数でアクセス可能
    • line には、改行も含まれている

一方で、書く方は、こんな感じ

with open(out_filename, 'w', encoding='utf-8') as file_out:
    # loop
        file_out.write(line)
  • with open だけど、w モード、つまり書込み+ text mode
  • file_out.write(data) で、line を書込み

ってことで、1行ずつファイルコピーするなら、こんな感じ。

def readwrite_file_in_lines(filename, out_filename):
    # ファイルを開いて
    print('Start: readwrite_file_in_lines')
    with open(filename, 'r', encoding='utf-8') as file_in:
        with open(out_filename, 'w', encoding='utf-8') as file_out:
            for line in file_in:   # line は改行含む
                print(line)        # なので、改行x2 出力される
                file_out.write(line)
        print('Done: readwrite_file_in_lines')

with 構文

I/O では、stream を開いて、一連の処理をした後で stream を閉じる必要があります。 ちょっと注意すべきは、プログラムが途中で例外などで途中終了する場合も、stream を閉じないといけないことです。 with 構文をつかってファイルを開いたり、ネットワークソケットを開いたりすると、 正常終了か例外による終了かに関わらず、with で開いたファイル・ソケットを自動で閉じてくれます。

with open(out_filename, 'w', encoding='utf-8') as file_out:
    # loop
        # ネットワーク経由でデータ取得。接続切れたら例外発生!
        line = RECEIVE_DATA_FROM_NETWORK()
        file_out.write(line)

今回はあまり取り上げませんが、python でも例外の仕組みがあります。ファイル操作に失敗した場合、例外が起きるのですが、その場合の処理が書けるのです。 例えばこんな感じ。

file_in = open(filename, 'rb')
try:
    while True:
        data = file_in.read(BUFSIZE)
        if not data:
            break
        sock.sendall(data)
except Exception as excep:
    # handle が書きたい場合
finally:
    file_in.close()

file の open, close 処理も含めて、明示的に書いています。 まあでも、単にファイルを開いて、例外の有無にかかわらずブロック終了時に閉じるってだけなら、 with 構文の方がはるかにシンプルにプログラムが書けますよね。