open関数とファイルオブジェクトとその他もろもろ

初回公開:2017/11/05
最終更新:未

【 目次 】

いつものように環境はwindos10にてテスト

pythonのファイルアクセスについてググってみると

open関数

まず、open関数を使ってファイルをオープン。

f = open(filename, mode)
filename
最初の引数はファイル名の入った文字列です。
mode
r:読み出し専用
w:書き込み専用 (同名の既存のファイルがあれば消去される)
a: はファイルを追記用に開く(ファイルに書き込まれた内容は自動的にファイルの終端に追加さる。)
r+:読み書き両用で開く
  'b' をつけるとバイナリモードで開く(通常はテキストモード )

mode 引数は省略可能で、省略された場合には 'r' であると仮定される。

改行文字の扱い

プラットフォーム固有の行末記号 (Unix では \n 、Windows では \r\n) をただの \n に変換するのがデフォルトの動作です。
テキストモードの書き込みでは、 \n が出てくる箇所をプラットフォーム固有の行末記号に戻すのがデフォルトの動作です。
このためバイナリファイルをテキストモードで開くとバイナリデータを破壊する恐れがあり危険。
Unix では、 'b' を追加しても何も影響がない

デフォルトのエンコード

エンコーディングが指定されなければ、デフォルトはプラットフォーム依存です (open() を参照してください) 。

open関数のリファレンス

公式ドキュメントのopen関数は

Python 3ではname(filename)やmode引数の他に多くの引数を指定する事ができ、encoding引数を使って文字コードのエンコードを指定する事ができる。
従ってPython 3ではエンコードの指定は簡単だが、python2の場合はencoding引数が用意されていない。

python2の場合はどうするのだろうか?

ファイル入出力の簡単なサンプルコード

まず、半角文字テキストファイルの入出力のサンプルコードを示す。

半角テキストのファイル入出力 - python2

# 半角テキストのファイル出力

b_str="""
sabcdef
1234567
xya123
"""
f = open("asc_sample.txt",'w')
f.write(b_str)
f.close()

# 半角テキストのファイル入力

f = open("asc_sample.txt",'r')
str=f.read()
f.close()
print str

実行結果

sabcdef
1234567
xya123

open関数はファイルオブジェクトを返す。

print f.__class__.__name__

実行結果

file

エンコードされたファイルの扱いと標準のopen関数

open関数を使ってアスキーテキストを読み込んで表示させる事は可能だが、 ではutf-8等のエンコードされたファイルの入出力はどうなるだろう。

python3

以下の内容をテキストエディタで入力してutf-8で保存

uni_sample.txt

あいうえお
かきくけこ
abcd1234

前述のとおり、python3ではopen関数にencoding引数が用意されているので open関数のfilename引数にutf-8で保存したファイルを指定して

f = open("uni_sample.txt",'r',encoding="utf-8")
str=f.read()
f.close()
print(str)

実行結果

あいうえお
かきくけこ
abcd1234

python2

python2にはopen関数にencoding引数が用意されていない。
とりあえず、encoding引数を指定せずにプログラムを実行してみる。

f = open("uni_sample.txt",'r')
str=f.read()
f.close()
print str

実行すると

実行結果

縺ゅ>縺・∴縺・
縺九″縺上¢縺・
abcd1234

文字化けを起こしてしまう。

readメソッドの戻り値を格納したstr変数の型を調べてみると

print type(str)

実行結果

<type 'str'>

readメソッドの戻り値はバイト文字列であるstr型の値となっている。
str型に格納されてユニコード文字列をユニコードに変換して表示させるにはdecodeメソッドを使って

u_str=str.decode(encoding="utf-8")
print type(u_str)
print u_str

実行結果

<type 'unicode'>
あいうえお
かきくけこ
abcd1234

decodeメソッドの部分についてはunicode関数を使って以下のように置き換える事もできる。

u_str=unicode(text_data, "utf-8")

今度は、ファイルの書き込みについてのサンプルを示そう。

u_str=u"""あいうえお
かきくけこ
abcd1234
"""
f = open("uni_sample_out.txt",'w')
b_str=u_str.encode("utf-8")
f.write(b_str)
f.close()

以下のサイトも参照。

codecsモジュールを利用する。 - python2でエンコードされたファイルを扱うためのopen関数

まわりくどい説明になったが、python2でエンコードされたファイルを標準のopen関数を使って読み込むのは面倒である。
このため、python2でエンコードされたファイルを読み込むには通常はcodecsモジュールに用意されている標準関数のopen関数とは別のopen関数を使うのが一般的なようだ。

import codecs

f = codecs.open("uni_sample.txt",'r',encoding='utf-8')
str=f.read()
f.close()
print str

codecsモジュールのopen関数はStreamReaderWriterクラスのインスタンスを返す。

StreamReaderWriterクラスには標準のoen関数が返すfileオブジェクトと同名のメソッドが用意されていてfileオブジェクトと同じように使えるようだ。

python3にもcodecsモジュールが使える。

ioモジュールを利用する。 - python2でpython3なみのopen関数を

codecsモジュールと同様にioモジュールにもopen関数が。

ioモジュールは「Python 3.x のために設計されたもの」なのだそうで、これがpython2.7でも使える。

import io

f = io.open("uni_sample.txt",'r',encoding='utf-8')
str=f.read()
f.close()
print str

print f.__class__.__name__

実行結果

あいうえお
かきくけこ
abcd1234

TextIOWrapper

ioモジュールのopen関数はTextIOWrapperクラスのインスタンスを返すようだ。

open関数とファイルオブジェクト

これまでみてきたように、open関数には標準関数とcodecsモジュールの関数,ioモジュールの関数とがありどう使い分ければよいのか混乱してしまう。

ここで、これらのopen関数が返すファイルオブジェクトのクラスを整理してみよう。   (pythonではopen関数が返すオブジェクトの総称をファイルオブジェクトと呼んでいるみたい。)

open関数が返すオブジェクトのクラス

以下のコードを使って各open関数が返すオブジェクトのクラスを表示させてみると面白い結果になる。

python2の場合

import platform
print platform.python_version()
filename="sample.txt"

print u"=========================== 標準のopen関数"
f = open(filename,'r')
print f.__class__.__name__
f.close()

f = open(filename,'rb')
print f.__class__.__name__
f.close()

filename="sample.txt"
f = open(filename,'w')
print f.__class__.__name__
f.close()

f = open(filename,'wb')
print f.__class__.__name__
f.close()

print u"=========================== codecsモジュールのopen関数"
import codecs

f = codecs.open(filename,'r')
print f.__class__.__name__
f.close()

f = codecs.open(filename,'rb')
print f.__class__.__name__
f.close()

f = codecs.open(filename,'w')
print f.__class__.__name__
f.close()

f = codecs.open(filename,'wb')
print f.__class__.__name__
f.close()

f = codecs.open(filename,'r',encoding='utf-8')
print f.__class__.__name__
f.close()

f = codecs.open(filename,'w',encoding='utf-8')
print f.__class__.__name__
f.close()

print u"=========================== ioモジュールのopen関数"
import io

f = io.open(filename,'r')
print f.__class__.__name__
f.close()

f = io.open(filename,'rb')
print f.__class__.__name__
f.close()

f = io.open(filename,'w')
print f.__class__.__name__
f.close()

f = io.open(filename,'wb')
print f.__class__.__name__
f.close()

f = io.open(filename,'r',encoding='utf-8')
print f.__class__.__name__
f.close()

f = io.open(filename,'w',encoding='utf-8')
print f.__class__.__name__
f.close()

実行結果

2.7.8
=========================== 標準のopen関数
file
file
file
file
=========================== codecsモジュールのopen関数
file
file
file
file
StreamReaderWriter
StreamReaderWriter
=========================== ioモジュールのopen関数
TextIOWrapper
BufferedReader
TextIOWrapper
BufferedWriter
TextIOWrapper
TextIOWrapper

python3の場合

import platform
print(platform.python_version())
filename="sample.txt"

print("=========================== 標準のopen関数")
f = open(filename,'r')
print(f.__class__.__name__)
f.close()

f = open(filename,'rb')
print(f.__class__.__name__)
f.close()

filename="sample.txt"
f = open(filename,'w')
print(f.__class__.__name__)
f.close()

f = open(filename,'wb')
print(f.__class__.__name__)
f.close()

print("=========================== codecsモジュールのopen関数")
import codecs

f = codecs.open(filename,'r')
print(f.__class__.__name__)
f.close()

f = codecs.open(filename,'rb')
print(f.__class__.__name__)
f.close()

f = codecs.open(filename,'w')
print(f.__class__.__name__)
f.close()

f = codecs.open(filename,'wb')
print(f.__class__.__name__)
f.close()

f = codecs.open(filename,'r',encoding='utf-8')
print(f.__class__.__name__)
f.close()

f = codecs.open(filename,'w',encoding='utf-8')
print(f.__class__.__name__)
f.close()

print("=========================== ioモジュールのopen関数")
import io

f = io.open(filename,'r')
print(f.__class__.__name__)
f.close()

f = io.open(filename,'rb')
print(f.__class__.__name__)
f.close()

f = io.open(filename,'w')
print(f.__class__.__name__)
f.close()

f = io.open(filename,'wb')
print(f.__class__.__name__)
f.close()

f = io.open(filename,'r',encoding='utf-8')
print(f.__class__.__name__)
f.close()

f = io.open(filename,'w',encoding='utf-8')
print(f.__class__.__name__)
f.close()

実行結果

3.4.2
=========================== 標準のopen関数
TextIOWrapper
BufferedReader
TextIOWrapper
BufferedWriter
=========================== codecsモジュールのopen関数
TextIOWrapper
BufferedReader
TextIOWrapper
BufferedWriter
StreamReaderWriter
StreamReaderWriter
=========================== ioモジュールのopen関数
TextIOWrapper
BufferedReader
TextIOWrapper
BufferedWriter
TextIOWrapper
TextIOWrapper

面倒臭いので、すべての場合について確認した訳では無いが、python2の場合は標準のopen関数とencoding引数を指定しないcodecsモジュールのopen関数はfileクラスのオブジェクトを,
encodingを指定したcodecsモジュールのopen関数はStreamReaderWriterクラスのオブジェクトを返すようだ。
そして、ioモジュールのopen関数はテキストモードでopenするとTextIOWrapperクラスのオブジェクトを、バイナリーモードでopenするとBufferedReader(読み込みモード)かBufferedWriter(書き込みモード)を返すようだ。

言い方を変えると、python2では標準のopen関数はfileクラスのオブジェクトを、python3の標準のopen関数はioモジュールのopen関数と同じオブジェクトを返す。
そしてcodecsモジュールのopen関数は、python2,python3ともにencodingを指定しない場合は標準のopen関数と同じクラスのファイルオブジェクトを、encodingを指定した場合はStreamReaderWriterクラスのオブジェクトを返すようだ。

曖昧な結論になるが、codecsモジュールはエンコーディングの処理に,io モジュールはストリーム処理に主点がおかれていて、
codecsモジュールのopen関数は、python2の標準のopen関数ではサポートされていなかったエンコーディングの処理もできるopen関数という位置づけだったのではないか。
そして、その後python3の標準のopen関数として新たに設計し直されたのがioモジュールのopen関数という事になる。
codecsモジュールとioモジュールは、python2とpython3の互換性のためにpython2とpython3のどちらでも使えるようになっているものと推察される。

どのopen関数を使いえば良いのだろうか?

では、これらの3つのopen関数をどう使い分ければ良いのかというと、私の個人的な見解としては、python2の場合はエンコーディングの処理が必要ない場合は標準のopen関数をエンコーディングが必要な場合には実績のあるcodecsモジュールのopen関数を。
そして曖昧な表現になるが、何らかの理由でpython3と同様の機能が必要な場合にはio モジュールのopen関数を使うという事になる。

python3の場合は標準のopen関数に新たに設計し直されたio モジュールのopen関数が組み込まれているので標準のopen関数だけで十分だろう。
もしpython2との互換性を重視する等何らかの理由がある場合はcodecsモジュールのopen関数を試してみるという事になるのでは。

ファイルオブジェクトの操作

重複するがこれまでのopen関数とファイルオブジェクトの公式ドキュメントのありかをまとめてみる。

python2とpython3とでファイルをオープンするには標準のopen関数とcodecsモジュールのopen関数,io モジュールのopen関数がある。

標準のopen関数

codecsモジュールのopen関数

ioモジュールの関数のopen関数

python3のioモジュールの関数のopen関数は標準のopen関数と統合されている。

ファイルオブジェクトとは

あらためて、ファイルオブジェクトとは何なんだろうか?

  • 用語集 — Python 2.7.13 ドキュメント

    file object (ファイルオブジェクト) 下位のリソースへのファイル志向 API (read() や write() メソッドを持つもの) を公開しているオブジェクトです。ファイルオブジェクトは、作成された手段によって、実際のディスク上のファイルや、その他のタイプのストレージや通信デバイス (例えば、標準入出力、インメモリバッファ、ソケット、パイプ、等) へのアクセスを媒介できます。ファイルオブジェクトは file-like objects や streams とも呼ばれます。 ファイルオブジェクトには3つの種類があります: 生のバイナリーファイル、バッファされたバイナリーファイル、そしてテキストファイルです。 これらのインターフェースは io モジュール内で定義されています。 ファイルオブジェクトを作成する標準的な方法は open() 関数を利用することです。

  • 用語集 ? Python 3.6.1 ドキュメント

    (ファイルオブジェクト) 下位のリソースへのファイル志向 API (read() や write() メソッドを持つもの) を公開しているオブジェクトです。ファイルオブジェクトは、作成された手段によって、実際のディスク上のファイルや、その他のタイプのストレージや通信デバイス (例えば、標準入出力、インメモリバッファ、ソケット、パイプ、等) へのアクセスを媒介できます。ファイルオブジェクトは file-like objects や streams とも呼ばれます。 ファイルオブジェクトには実際には 3 種類あります: 生の バイナリーファイル、バッファされた バイナリーファイル、そして テキストファイル です。インターフェイスは io モジュールで定義されています。ファイルオブジェクトを作る標準的な方法は open() 関数を使うことです。 file-like object file object と同義です。

それぞれのopen関数は異なるクラスのファイルオブジェクトを返すが、概ね同じファイルアクセスのメソッドを使ってファイルを操作できるようだ。

ファイルオブジェクトのクラス

あらためて、各ファイルオブジェクトのクラスのドキュメントへのリンクをピックアップ

fileクラス

python3ではfileクラスは廃止されているもよう。

StreamReaderWriterクラス

TextIOWrapperクラス

BufferedReaderクラス

ファイル操作に関するサンプルコードと参考サイト

ファイル操作に関するサンプルコードもと思ったのだが、そろそろ調べるのも面倒になったので参考サイトのみをあげておく。

fileオブジェクトにはファイルの入出力をおこなうメソッドが用意されている。

テキストファイルの読み込みについては
readメソッド:全文読み込み
readlineメソッド:1行ずつ読み込み
readlines:すべての行をリスト形式で読み込み

テキストファイルの書き込みについては
writeメソッド:全文書き込み
writelineメソッド:1行ずつ書き込み
writelines:すべての行をリスト形式で書き込み

バイナリファイルのアクセス

バイナリファイルのアクセスのサンプルを

python2

# バイナリーデータのファイル出力

b_str_out=b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a'
bin_file_name="sample.bin"
f = open(bin_file_name,'wb')
f.write(b_str_out)
f.close()

# バイナリーデータのファイル入力

f = open(bin_file_name,'rb')
b_str_in=f.read()
f.close()
print(b_str_in.__class__.__name__)

for c in b_str_in:
   print "{0:2x}".format(ord(c)),
print

テキストファイルと同様、ファイルの書き込みにはwriteメソッド,読み込みにはreadメソッドが利用できる。

実行結果

str
e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a

python3の場合、ファイルの書き込みのコードは同じなので読み込みのコードのみを示す。

python3

# バイナリーデータのファイル入力

f = open(bin_file_name,'rb')
b_str_in=f.read()
f.close()
print(b_str_in.__class__.__name__)

for i in b_str_in:
   print("{0:2x}".format(i),end=" ")
print()

実行結果

bytes
e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a

注目すべき点はreadメソッドの戻り値の型がpython2の場合はstr型なのに対してpython3の場合はbytes型になる点。
python2のstr型はバイト文字列なので当然といえばとうぜんなのだが。

また、ファイルアクセスとは関係ないが、for文の変数iやcに割り当てられる値の型がpython2の場合はバイト文字列なのに対して、python3の場合は数値型になる。
そして、print文にも違いが

参考URL

ファイルアクセスに付随する雑多な話題

python 標準入力 標準出力 パイプ

raw_input

標準出力での文字コードの扱い

eofの判定

  • ファイル - Dive Into Python 3 日本語版

    少し意外かもしれないが、再びファイルの読み込みを行っても例外は発生しない。Pythonは、ファイルの終端 (EOF) からさらに読み込もうとしてもエラーとは見なさず、単に空の文字列を返すだけなのだ。

  • pythonでEOFを明示的に取得する - Qiita

    readline()の返り値が空文字のとき,EOFまで達しています.
    ...
    ファイルオブジェクトからnext関数を呼び出した場合,EOFに達するとStopIterationという例外が発生します.これを捉えることで,EOFを検知できます.

with

  • Pythonのwith構文の使い方 | UX MILK

    with文を使うことによって、ファイルの閉め忘れやメモリの解放し忘れを防ぐことができ、例外処理にも強いより安全なプログラムを書くことができます。

with open("sample.txt", "r") as f:
    print f.read()

pythonにおけるwindowsのpathの区切り文字の扱い。

python path windows

デレクトリの区切り文字は\なので文字列中に記述するには\\と記述しなくてはならない。

芸がない記述は

path = "C:\\documents\\nori\\tama"

unix風にデレクトリの区切りに/ も使える。

path = "C:/documents/nori/tama"

でもどうしても\を使いたい場合は

path = r"C:\documents\nori\tama"

ただし末尾に'\'を付けるとエラーになる。

path = r"C:\documents\nori\tama\"

デレクトリとパスに対する操作

ファイルとディレクトリへのアクセス

ディレクトリとファイルの判別

ファイルの一覧を取得

ファイルのコピー

ファイル比較

Windows における日本語ファイル名に対する扱い

文字列をファイルのように扱う

ページのトップへ戻る