Pythonでファイルを読み込む

保存されたデータを扱うために、ファイル操作はプロのPythonプログラマーにとって核となる知識に属します。Pythonの初期のリリースから、ファイルへのデータの読み取りと書き込みの両方がPythonの組み込み機能です。CやJavaのような他のプログラミング言語と比較すると、それは非常にシンプルで、数行のコードしか必要としません。さらに、それを適切に行うために余分なモジュールをロードする必要はありません。

Pythonにおけるファイルの基本

ファイルを操作するための一般的なメソッドは、ファイルを開くための open() 、ファイルの現在位置を与えられたオフセットに設定するための seek() 、ファイルオブジェクトを使い終わったら閉じるための close() です。open()` メソッドは、ファイルオブジェクトを表すファイルハンドルを返し、これを用いてファイルにアクセスし、読み込み、書き込み、追記を行います。

ファイルを読み込むためにオープンするとき、Python はそのファイルがシステムでどのようにオープンされるべきかを正確に知る必要があります。2つのアクセスモードが利用可能です – 読み込みとバイナリモードでの読み込み。使用するフラグは rrb で、組み込みの open() メソッドでファイルを開く際に指定する必要があります。最初のモードでは、改行を表す “CR” (キャリッジリターン) や “LF” (ラインフィード) といった特殊文字を解釈する。一方、バイナリモードでは、データを解釈せずにそのまま保存する raw モードでデータを読み込むことが可能である。

ファイルを開くと、open() メソッドはファイルオブジェクトを返します。これらのファイルオブジェクトは read(), readline(), write(), tell(), seek() などのメソッドを持っています。ファイルオブジェクト(あるいはファイルライクオブジェクト)の中には、ここに挙げたものよりも多くのメソッドを持つものがありますが、これらは最も一般的なものです。すべてのファイルオブジェクトがすべてのファイルメソッドを実装する必要はありません。

今回は、Pythonでファイルを読み込む方法を例題を通して説明します。例としては、ファイルを一行ずつ読む、チャンク(一度に定義された行数)で読む、ファイルを一度に読む、などがあります。また、ファイル全体を検索せずに、ファイルから特定の行だけを読む方法も紹介します。

ファイルを一行ずつ読み込む

最初の例は、CとC++の2つのプログラミング言語からヒントを得ています。open()メソッドでファイルを開き、readline()メソッドでファイルを一行ずつ読み、読み終わったらすぐにその行を出力するという非常にシンプルなものです。ここでは、readline()メソッドがデータを返し続ける限り、ファイルからの読み込みを継続するwhileループを使用しています。ファイルの終わり (EOF) に達すると、while` ループは停止してファイルオブジェクトが閉じられ、他のプログラムが使用できるようにリソースが解放されます。

# define the name of the file to read from
filename = "test.txt"


# open the file for reading
filehandle = open(filename, 'r')
while True:
    # read a single line
    line = filehandle.readline()
    if not line:
        break
    print(line)


# close the pointer to that file
filehandle.close()


リスト1

リスト1では、明示的にファイルをオープンし、クローズしています(それぞれ5行目と14行目)。Pythonインタプリタは、Pythonプログラムの実行が終了すると、開いたファイルを自動的に閉じますが、 close() を使って明示的にファイルを閉じることは、良いプログラミングスタイルであり、忘れてはいけないことなのです。

改良点として、Python 2.3 では、便利なイテレータプロトコルが導入されました。これにより、 readline ループを次のように簡略化することができます。

# define the name of the file to read from
filename = "test.txt"


for line in open(filename, 'r'):
    print(line)


リスト2

ここでは、 for ループと in イテレータを組み合わせて使用しています。リスト 2 の 4 行目でファイルをオープンしています。現在の行は in イテレータの助けによって特定され、ファイルから読み込まれて、5行目でその内容が stdout に出力されます。Python はファイルがスコープ外に出たときに、ファイルのオープンとクローズを代行してくれます。非効率的ではありますが、これによってファイルハンドルを扱う必要がなくなります。

残念ながら、上のコードはあまり明確ではなく、ファイルを閉じる処理をPythonの内部ガベージコレクションに依存しています。Python 2.5 で導入された with コマンドは処理全体をさらにカプセル化し、スコープ付きのコードブロック全体で一度だけファイルを開いたり閉じたりする処理を行います。リスト3は with コマンドの使い方を示しています。

# define the name of the file to read from
filename = "test.txt"


with open(filename, 'r') as filehandle:
    for line in filehandle:
        print(line)


リスト3

withステートメントとopen()コマンドの組み合わせで、ファイルを一度だけ開きます (4行目)。成功するとforループが実行され、その内容がstdout` に出力されます (5行目、6行目)。

さらに、 with 文の使用には副作用があります。内部的には、Pythonインタプリタが tryfinally-block を作成して、ファイルからの読み込みをカプセル化します。リスト4は、Pythonの内部で with コードブロックによって本質的に何が起こっているかを示しています。

try:
    filehandle = open(filename, 'r')
    # do something
finally:
    filehandle.close()


リスト4

ファイルを行のまとまりとして読み込む

これまで、ファイルを一行ずつ処理してきました。これは巨大なファイルに対してはかなり遅いので、複数の行を同時に読むことで改善することができます。これを実現するために、itertoolsモジュールの islice() メソッドが活躍します。また、これはイテレータとして動作し、n行からなるデータの塊を返します。ファイルの最後では、結果はより短くなり、最終的には空のリストを返します。

from itertools import islice


# define the name of the file to read from
filename = "test.txt"


# define the number of lines to read
number_of_lines = 5


with open(filename, 'r') as input_file:
    lines_cache = islice(input_file, number_of_lines)

    for current_line in lines_cache:
        print (current_line)


リスト5

ファイルから特定の行を読み取る

上記のメソッドを使用すると、ファイルから特定の行を読み取るなど、他の便利なアクションを実行することもできます。これを行うには、カウンターを使用し、ファイルを反復している間に適切な行に到達したら、それを表示します。

# define the name of the file to read from
filename = "test.txt"


# define the line number
line_number = 3


print ("line %i of %s is: " % (line_number, filename))


with open(filename, 'r') as filehandle:
current_line = 1
    for line in filehandle:
        if current_line == line_number:
            print(line)
            break
        current_line += 1


リスト6

リスト6は簡単に理解できるはずですが、これまでの例より少し長くなっています。linecacheモジュールを使用すれば、短くすることができます。リスト7は getline() メソッドを使ってコードを簡略化する方法を示しています。もし、要求された行番号がファイル内の有効な行の範囲外であれば、getline() メソッドは代わりに空の文字列を返します。

# import linecache module
import linecache


# define the name of the file to read from
filename = "test.txt"


# define line_number
line_number = 3


# retrieve specific line
line = linecache.getline(filename, line_number)
print ("line %i of %s:" % (line_number, filename))
print (line)


リスト7

ファイル全体を一度に読む

最後に、先ほどの例とは全く異なるケース、つまり、ファイル全体を一度に読み込む場合について見てみましょう。ほとんどの場合、コンピュータにはファイル全体をメモリに読み込むのに十分なスペースがあるはずだということを覚えておいてください。リスト8では、 with ステートメントと read() メソッドを組み合わせて使用しています。この例では、ファイルの内容をデータストリームとして読み込むために read() を使用します。

# define the name of the file to read from
filename = "test.txt"


with open(filename, 'r') as filehandle:
    filecontent = filehandle.read()
    print (filecontent)


リスト8

Pythonには readlines() メソッドもあり、これは最初の例で出てきた readline() メソッドと似ています。read()`とは対照的に、ファイルのコンテンツはリストに格納され、コンテンツの各行がアイテムになります。リスト9は、そのデータにアクセスする方法を示しています。

# define the name of the file to read from
filename = "test.txt"


with open(filename, 'r') as filehandle:
    filecontent = filehandle.readlines()
    for line in filecontent:
        print (line)


リスト9

readlines()はファイルから EOF になるまでコンテンツを読み込みますが、sizehint` パラメータ (読み込むバイト数) を指定して、読み込むコンテンツの量を制限することもできることを覚えておいてください。

結論

ファイルの内容を読み取る方法は、通常通り1つだけではありません。速度の面では、どれも多かれ少なかれ同じカテゴリに属します。どのソリューションが最適かについては、特定のユースケースに依存します。何が可能かを確認し、最も適した解決策を選択することは非常に有益だと考えています。

Pythonはファイル読み込みのプロセスを大幅に簡素化しますが、それでも時々トリッキーになることがあるので、その場合はPythonの公式ドキュメントを参照することをお勧めします。

リソース

  • Python でファイルを正しく読み込む方法、https://www.smallsurething.com/how-to-read-a-file-properly-in-python/
  • Python を使った大きなファイルの処理、http://www.blopig.com/blog/2016/08/processing-large-files-using-python/
  • Python itertools モジュール、https://docs.python.org/3.6/library/itertools.html
  • Python の linecache モジュール、https://docs.python.org/3.6/library/linecache.html

謝辞

Zoleka Hatitongweのサポートに感謝する。

タイトルとURLをコピーしました