HOWTO · Python

Python でファイルから特定の行を読み取る方法

Pythonのテキストファイルから特定の行を読み取る方法を紹介します。小さなファイルには`readlines()`、大きなファイルには`enumerate()`を使えます。同じファイルの行に繰り返しアクセスするには`linecache`が便利ですが、キャッシュはメモリを使用します。

このページの内容

Pythonのテキストファイルから特定の行を1行または複数行読み取るには、まず行番号が人向けの番号か、Pythonコードで使う番号かを確認します。人向けの行番号は通常1から始まりますが、リストのインデックスは0から始まります。小さなファイルなら、readlines()ですべての行を読み込み、インデックスで直接参照できます。大きなファイルなら、enumerate()で順に読み取れます。行番号を指定して何度もアクセスする場合は、linecacheを使えますが、ファイルデータをキャッシュするため、メモリ使用量が一定に抑えられる方法ではありません。

まず行番号の数え方を決める

人から3行目を指定された場合、対応するリストのインデックスは3 - 1、つまり2です。enumerate()も既定ではインデックスを0から始めます。一方、linecache.getline(path, 3)には、人向けの行番号である3を指定します。こうした数え方を明確にしておくと、1行ずれる間違いを防げます。テキストの各行には末尾の改行文字が含まれることが多いため、表示や比較の際にその空白が不要な場合だけ取り除きます。

小さなファイルからreadlines()で行を読み取る

readlines()は、存在する場合は改行文字も含め、すべての行をリストとして返します。ファイル全体がメモリに無理なく収まり、複数の位置にアクセスする可能性がある場合に便利です。次の自己完結した例では、一時テキストファイルを作成し、3行目を表示します。

このセクションのコードと表示されるラベルや出力は英語のままです。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    path = Path(folder) / "notes.txt"
    path.write_text("first\nsecond\nthird\nfourth\n", encoding="utf-8")
    with path.open(encoding="utf-8") as file:
        lines = file.readlines()
    print(lines[2].rstrip("\n"))

人向けの行番号でfirst_lineからlast_lineまで(両端を含む)を取得するには、スライスlines[first_line - 1:last_line]を使います。スライスの終端は範囲に含まれないため、人向けの最終行番号をそのまま終端インデックスにできます。指定された行が存在しない可能性がある場合は、インデックスを使う前にlen(lines)の範囲内か確認してください。

大きなファイルからenumerate()で行を読み取る

ファイルを直接反復処理すると、すべての行のリストを作らずに済みます。enumerate(file)は、各行を読み取るたびに0から始まるインデックスを返します。人向けの行番号を表示する場合は1を足します。後ろの行に到達するには、それより前の行も読み進める必要があります。そのため、この方法は離れた行へ直接移動するためではなく、メモリ使用量を抑えるのに役立ちます。

このセクションのコードと表示されるラベルや出力は英語のままです。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    path = Path(folder) / "events.txt"
    path.write_text("start\nready\nconnected\nfinished\n", encoding="utf-8")
    wanted = {2, 4}
    with path.open(encoding="utf-8") as file:
        for line_number, line in enumerate(file, start=1):
            if line_number in wanted:
                print(line_number, line.rstrip("\n"))
            if line_number >= max(wanted):
                break

ここではstart=1を指定しているため、ループ変数を人向けの行番号として扱え、選択条件を読みやすくできます。ファイルの行数が指定した位置に満たない場合、ループはその要求を表示しないまま終了します。

同じファイルの行に繰り返しアクセスするにはlinecacheを使う

ファイルの1始まりの行番号を使ってコードから何度も行を取得する場合は、linecache.getline(path, line_number)を使います。行はテキストとして返され、存在する場合は改行文字も含まれます。行が存在しない場合は空文字列が返ります。linecacheは再利用できるようファイル内容をキャッシュするため、同じファイルを繰り返し照会する場合に便利ですが、キャッシュデータはメモリを使用します。

このセクションのコードと表示されるラベルや出力は英語のままです。

import linecache
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    path = Path(folder) / "report.txt"
    path.write_text("header\nalpha\nbeta\n", encoding="utf-8")
    line = linecache.getline(str(path), 2)
    print(line.rstrip("\n"))
    linecache.clearcache()

例の最後にある明示的なclearcache()呼び出しは、キャッシュされた項目を解放します。ファイルが変更される場合はキャッシュの鮮度にも注意してください。プログラムでファイルを変更し、キャッシュデータを破棄してから行を再取得したい場合は、キャッシュをクリアします。

範囲、存在しない行、メモリ使用量を扱う

ファイル全体をメモリに保持するより、扱いやすいサイズのファイルへ直接アクセスすることを優先する場合は、readlines()を選びます。大きなファイルを少しずつ処理したり、指定した行まで読んだ時点で停止したりする場合は、enumerate()を選びます。ただし、後ろの行へ到達するには前の行を読み進める必要があります。キャッシュを使ってよい場合は、行番号を指定した繰り返しの要求にlinecacheを選べます。どの方法でもファイル末尾より後の行は取得できません。リストのインデックス指定ではIndexErrorが発生し、反復処理では該当行が見つからず、getline()は空文字列を返します。要求を検証するか、プログラムの用途に応じてその結果を処理してください。

テキストファイルは読み取りモードで開き、ブロックの終了時にファイルが閉じるようwith文を使うのがおすすめです。ファイルのエンコーディングが分かっている場合は、それを指定します。ファイル全体を読み込むと、行数や行の長さが増えるにつれて多くのメモリを使うことがあります。どの環境や処理にも当てはまるファイルサイズの基準はありません。ストリーミングでは読み取りループが保持する行数を抑えられますが、選んだ行を集めれば、その分のメモリは引き続き必要です。固定のサイズや速度の主張ではなく、アクセス方法と利用可能なメモリを基準に選びましょう。