[Python]同一ファイルに対して複数回の読み取り処理を行う方法[seekメソッド]
オープンしたファイルに対して中身の読み取りを2回行おうとfor文を2回実行するコードを記載したところ、2回目のfor文が処理されない(ように見える)状況ではまったことについて備忘的に記載。
※2023/05/02更新。2023/05/01初回投稿時はseekメソッドの存在知らず、複数回読み取りは無理という趣旨の投稿だったのを修正。
結論
一回のファイルオープンでファイル読み取りを2回(複数回)行う場合、seekメソッドを使ってファイル読み取り位置をコントロールすることで可能。
[理由]
ファイルを読み込んだ場合、どの行まで読み込んだかをマークしている模様。最後まで読み込むとEOFになるため、そのままだと2回目の処理時はすでにEOFということで処理はするものの、何も読み込まないという状況になる。
seekメソッドを利用することでファイル読み取り位置を戻して再度読み取りを行うことで対応可能。
本記事の下部に検証内容を記載。
対応方法
上記のとおりなので、seekを使ってファイル読み取り位置を戻す。
seekメソッドの使い方は以下。
ファイルオブジェクト.seek(オフセット値, 基準点)
・第一引数「オフセット値」
第二引数「基準点」からどれだけ進んだ位置に読み取り位置をセット
するか。第二引数が省略された時はファイルの先頭行からになる。
・第二引数「基準値」
ファイルのどの位置をベースにして計算するか、を指定。省略可。
seekメソッドを使わないのであれば対応方法としては以下のいずれか、になるかと。
[1] 毎回ファイルをオープンする。
[2] for文の中にfor文をネスとして処理する。
[3] ファイルオープン時の処理では配列に読み込んで、それ以降は配列に対して行う。
検証
テスト用読み込みファイル
読み込み用ファイル:test.txt
111
222
333
444
①.同一ファイルに対して複数回読み込みができない
下記の[結果_実際]のとおり、一回目のfor文しか出力されません。
[コード:test.py]
処理内容:オープンしたファイルを1回目、2回目のforともに最後まで読み込んで表示する
test = open('test.txt', 'r', encoding = 'utf8')
for i in test:
print("1st:" +str(i))
for r in test:
print("2nd:" + str(r))
test.close()
[結果_期待値]
1回目も2回目も処理されて出力される。
1st:111
1st:222
1st:333
1st:444
2nd:111
2nd:222
2nd:333
2nd:444
[結果_実際]
一回目のfor文の内容しか出力されない
1st:111
1st:222
1st:333
1st:444
②.ファイル読み込みによってどこまで読みこんだかをマーキングしているかを確認
①の状況から読み込み位置を保持しているのでは、と想定。
本当にそうか、を検証。
[コード:test_break.py]
処理内容:一回目のfor文で2行目まで読み込んだらbreakして抜ける。
test = open('test.txt', 'r', encoding = 'utf8')
c = 0
for i in test:
print("1st:" +str(i))
c = c +1
if c >=2:
break
for r in test:
print("2nd:" + str(r))
test.close()
[結果_期待値]
1回目のforでは2行目まで読み込んで、2回目のforでは続きの3行目から読み込む。
1st:111
1st:222
2nd:333
2nd:444
[結果_実際]
期待値の通りの結果になった。
1st:111
1st:222
2nd:333
2nd:444
③.seekメソッドを使用してファイル読み込み位置をコントロールできるかを確認
[コード:test_seek.py]
処理内容:一回目のfor文で最後まで読み込んだ後にseekメソッドで先頭行に戻して、二回目のfor文に突入。
test = open('test.txt', 'r', encoding = 'utf8')
for i in test:
print("1st:" +str(i))
test.seek(0)
for r in test:
print("2nd:" + str(r))
test.close()
[結果_期待値]
1回目のforも2回目のforも1行目から最終行まで読み込む。
1st:111
1st:222
1st:333
1st:444
2nd:111
2nd:222
2nd:333
2nd:444
[結果_実際]
期待値の通りの結果になった。
seekは第一
1st:111
1st:222
1st:333
1st:444
2nd:111
2nd:222
2nd:333
2nd:444