ユーニックス総合研究所

  • home
  • archives
  • python-file-write

Pythonでファイルへ書き込みを行う【テキストファイル、バイナリファイル】

  • 作成日: 2023-12-25
  • 更新日: 2023-12-28
  • カテゴリ: Python

Pythonでファイルへ書き込みを行うには?

Pythonでファイルにデータを書き込むには、open()関数とファイルオブジェクトのメソッド(write()など)を使います。
この記事ではこれらの関数やメソッドについて解説します。

ファイルへ書き込む手順

ファイルにデータを書き込む手順はPythonに限らず他の言語でも同様になっています。
基本的な流れは以下になります。

  • ファイルを開く
  • 開いたファイルへデータを書き込む
  • ファイルを閉じる

まず関数などでファイルを開き、その開いた結果のストリームやファイルオブジェクトに対してデータを書き込みます。
そしてデータの書き込みが終わったらファイルをちゃんと閉じるようにします。

意外と忘れがちなのがファイルを閉じるということです。
ファイルは開きっぱなしにするとプログラム的な不具合、たとえばファイルが開きっぱなしになるなどが起こることがあります。
ですので使い終わったファイルはちゃんと閉じるのが大事です。

open関数でファイルを開く

Pythonではファイルを開くときはopen()関数を使います。

open関数 — Python 3.12.1 ドキュメント

open()はPythonの組み込みの関数で、なにもインポートしなくても使うことができます。
open()の返り値はオープンモードによって変わります。
オープンモードがテキストファイルの時はio.TextIOWrapperが、バイナリファイルの書き込みの時はio.BufferedWriterが返されます。

# これがopen関数のすべてだ!  
open(  
    file,  # 開きたいファイルのパス  
    mode='r',  # ファイルのオープンモード  
    buffering= -1,  # バッファリングポリシー  
    encoding=None,  # テキストファイルのエンコーディング  
    errors=None,  # エラー文字列('strict'など)  
    newline=None,  # テキストファイルの改行の指定  
    closefd=True,  # ファイルディスクリプタをクローズするかどうかのフラグ  
    opener=None  # オープナー  
)  

open()はキーワード引数がいろいろありますが、よく使うのはfile, mode, encodingの3つだけです。
この3つを押さえておけば大抵の用は足りるかと思います。

file

fileに開きたいパスを指定します。ファイルを開くことが出来ない場合はOSErrorが送出されます。

mode

modeにはオープンモード、つまりファイルをどのようにして開くか? という文字列の指定を書きます。
書き込みに使われるオープンモードは以下になります。

  • 'w' ... テキストファイルの書き込みモード
  • 'a' ... テキストファイルの追記モード
  • 'wb' ... バイナリファイルの書き込みモード
  • 'ab' ... バイナリファイルの追記モード

ファイルをテキストファイルとして開き、新規に作成して書き込みしたい場合はwを使います。
このwモードの注意点としては、このモードはすでに存在しているファイルであっても中身を空にしてファイルを開くことです。
ですのでうっかり大事なファイルをwモードで開いて空にしてしまわないように注意しましょう。

ファイルをテキストファイルとして開き、ファイルの末尾に追記したい場合はaを使います。
このaはストリームのポジションをファイル末尾にして開きますので、シークなどせずに書き込むだけでファイル末尾に追記できます。

wbwのバイナリファイル版です。
abaのバイナリファイル版になります。

encoding

エンコーディングはテキストファイルの場合に使われます。
encodingUTF-8を指定するとopen()はファイルをUTF-8のエンコーディングで開きます。
CP932にした場合はCP932です。

このencodingは指定しなかった場合は環境のデフォルトのエンコーディングが指定されます。
Windowsの場合はCP932で、Linuxの場合はUTF-8です。
ですので、マルチプラットフォームなコードを書きたい場合は、このencodingを指定するのを忘れないようにしましょう。
たとえばUTF-8で統一するならencodingUTF-8を指定しておきます。

open('file.txt', 'w', encoding='utf-8')  

withを使った自動クローズ

open()で開いたファイルオブジェクトは使い終わったらクローズしないといけません。

fout = open('file.txt', 'w')  
fout.close()  

これを自動でやってくれるのがwithです。
withを使うとスコープが切れたら自動的にファイルがクローズされます。
ですので明示的なclose()が必要ありません。

with open('file.txt', 'w') as fout:  
    pass  

上記の場合、foutがファイルオブジェクトです。

返り値

open()の返り値はテキストファイルの場合はio.TextIOWrapperで、バイナリファイルの場合はio.BufferedWriterが返されます。
これら2つは違うオブジェクトですが、インターフェースがある程度統一されているので、使用者は特に意識しなくても同じ認識で使うことが可能になっています。

with open('file.txt', 'w') as fp:  
    print(type(fp))  
    # <class '_io.TextIOWrapper'>  

with open('file.txt', 'wb') as fp:  
    print(type(fp))  
    # <class '_io.BufferedWriter'>  

io.TextIOWrapper — Python 3.12.1 ドキュメント
io.BufferedWriter — Python 3.12.1 ドキュメント

データの書き込み

テキストファイルへの書き込み

テキストファイルを新規作成し、書き込みを行うには以下のようなコードを書きます。

with open('file.txt', 'w', encoding='utf-8') as fout:  
    fout.write('hello')  

上記の場合、file.txtというファイルが新規作成されてhelloという文字列が書き込まれます。
書き込みにはファイルオブジェクトのwrite()メソッドを使います。
file.txtがすでに存在する場合はそのファイルの中身が空になります。

ちなみにfoutという変数はfile outputの略です。この命名は慣例的に使われることがあります。
逆はfinfile inputfile pointerfpというのもよく使われます。

テキストファイルへの追記

テキストファイルを開き、追記を行うには以下のようなコードを書きます。

with open('file.txt', 'a', encoding='utf-8') as fout:  
    fout.write('world')  

上記のコードでfile.txtの中身がhelloだった場合は、追記によって中身がhelloworldに更新されます。
ファイルが存在しなかった場合は新規でファイルが作成されます。

バイナリファイルへの書き込み

バイナリファイルを新規作成し、書き込みを行うには以下のようなコードを書きます。

with open('file.dat', 'wb') as fout:  
    fout.write(b'hello')  

バイナリファイルのオープンではencodingは必要ないことに注意してください。
また書き込むデータもb'hellobプリフィックスを付けてバイト列にしていることに注目してください。
file.datがすでに存在する場合はそのファイルの中身が空になります。

バイナリファイルへの追記

バイナリファイルへ追記を行うには以下のようなコードを書きます。

with open('file.dat', 'ab') as fout:  
    fout.write(b'world')  

file.datの中身がhelloだった場合は追記によって中身がhelloworldに更新されます。
ファイルが存在しなかった場合は新規作成されます。

バッファのフラッシュ

ファイルオブジェクトはバッファリングと言って、データをある程度溜めてから一括で処理するということを行うことがあります。
この「一括で処理する」ことを「フラッシュ」と言ったりもします。
書き込みの場合は「一括で書き込む」という意味になるかと思います。

このフラッシュをマニュアルで行いたい場合は、ファイルオブジェクトのメソッドのflush()を使います。
以下はフラッシュを使ったサンプルです。

import time  

with open('file.dat', 'wb') as fout:  
    for i in range(10):  
        fout.write(b'1')  
        fout.flush()  
        time.sleep(1)  

上記のコードを実行すると、1秒ごとにfile.datにデータが書き込まれてフラッシュされます。
プログラムの実行中にfile.datの中身をcattypeなどで見ると、中身が刻々と変わっていくのがわかると思います。

このフラッシュの概念は標準ファイルオブジェクトにも存在します。
標準ファイルオブジェクトはsys.stdoutsys.stderrなどです。
print()は内部でsys.stdoutにデータを書きこんでいますので、print()でバッファをフラッシュしたい場合はsys.stdout.flush()などを実行するといいでしょう。
あるいはprint('Hello', flush=True)でもフラッシュを実行できます。

io.BufferedWriterの場合は、フラッシュのタイミングは以下になります。

  • 保留している全てのデータに対してバッファが足りなくなったとき
  • flush()が呼ばれたとき
  • seek()がリクエストされたとき
  • ファイルオブジェクトが閉じられたり破棄されたとき

io.BufferedWriter — Python 3.12.1 ドキュメント

エラーの処理

open()でエラーを処理したい場合は例外をキャッチします。
open()OSErrorを継承した例外を送出しますので、これでハンドリングできます。

たとえばファイルが存在しなかった場合はFileNotFoundErrorが送出されます。

try:  
    with open('nothing.dat', 'r') as fin:  
        pass  
except FileNotFoundError as e:  
    print(e)  
    # [Errno 2] No such file or directory: 'nothing.dat'  

しかし、書き込みも追記もファイルを新規作成しますので、このエラーになることはない気がします。
よくあるのはパーミッションエラーでしょう。

try:  
    with open('permission.dat', 'w') as fin:  
        pass  
except PermissionError as e:  
    print(e)  
    # [Errno 13] Permission denied: 'permission.dat'  

パーミッションエラーはファイルに対する操作の権限がない時に発生します。
たとえばファイルの所有者が他のユーザーで、そのユーザーのファイルに対する書き込み権限を持っていない場合は、このようなエラーが出ます。
パーミッションエラーを人為的に発生させたい場合、Ubuntuなどではファイルの所有者をrootにするとエラーを発生させられます。
もちろんスクリプトの実行はroot以外で行う必要があります。

$ # permission.dat のファイルの所有者とグループを root に変更  
$ sudo chown root:root permission.dat  

大量データの書き込み

データ量が多い場合はバッファにデータを溜めてそれを一気に書き込むか、あるいは分割されているデータを随時書き込んでいくかになります。
ですがバイナリのファイルオブジェクトはデフォルトでバッファリングをしますので、わざわざバッファを作ってそれにデータを溜めなくても、勝手にバッファリングしてくれる理屈になります。
よってメモリを節約したい場合は随時書きこんで行った方がいいのかもしれません。

# 大量データの書き込み  
with open('file.dat', 'wb') as fout:  
    for _ in range(100000):  
        fout.write(b'1')