[Python]ファイルの読み込みと書き込み

Python

Pythonはあるけどテキストエディッタがない仮想環境で、ファイルの書き込み方を調べていたのですが、結局ファイルの開き方から読み込み方法まで調べないと満足に使えず、このページに一通りまとめました。

これならテキストエディタをインストールした方が早かったかもしません。
ただ、ファイルを使ったプログラミングはかなり応用が効くだろうし、覚えておいて損はないですよね。

このページでは、ファイルの開き方、ファイルの読み込み(すべて読み込む、特定の文字数、一行ごとの読み込み)、ファイルの位置とその移動方法、書き込み方を見ていきます。

参考サイト 7.2.ファイルを読み書きする[docs.python.org]

私の環境
WSL2 - Ubuntu 20.04 LTS
Python 3.10.0

 Androidアプリを作成しました。
 感情用のメモ帳です。

スポンサーリンク
スポンサーリンク

ファイルの開き方

ファイルを開くには組み込みのopen関数を使います。

f = open(ファイルのパス, モード)

fは変数名で何でも構いません。ファイルオブジェクトが入ります。

open関数の2つ目の引数は、どのモードで開くか指定します。

'r' 読み込み専用(省略したときのデフォルト値
'w' 書き込み専用(新規作成)
'x' 書き込み用ファイルを作成するが、同名のファイルがあればエラーを発生させる。
'a' 書き込み専用で、ファイル末尾に追記
'+' 読み込みに+で書き込みも可能、書き込みに+で読み込み可能
't' テキストモード(省略したときのデフォルト値
'b' バイナリモード
があります。

これらのモードを組み合わせて指定します。
'r', 'rb+', 'wb', 'x+' など。

注意点が複数あります。

  • openで開いたら、closeメソッドで閉じる必要がある。
    (閉じ忘れるとメモリが開放されなかったり、ファイルにアクセスができなくなることもある)
  • モードに'w'や'w+'を選択し、ファイルパスに同名のものがある場合、元のファイルは消えてしまう。
  • テキストファイル以外はバイナリモードで開く(壊れる可能性があるから)。

処理が終わったらファイルを閉じる必要がありますが、以下のように閉じます。

f.close()

openとcloseはセットですが、open関数を呼ぶときにwith文と一緒に使うと、with文を抜けたときに自動でclose()してくれます。

with open(ファイルパス, モード) as f:
    処理

開いたファイルをfという変数に入れます(もちろんfである必要はありません)。

変数にはファイルオブジェクトが入っていて、変数を使った処理をインデントして書いていき、終わったらwith文を抜けます。

実際にやってみましょう。

カレントディレクトリ内にある「test.txt」を読み込みます。
テキストファイルには「Hello World」とあるだけです。

$ python
Python 3.10.0
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('test.txt') as f:
...     print(f.read())
... 
Hello World
>>> f.closed
True

※read()の説明は後ほど。

「Hello World」が表示されましたね。

ファイルが閉じられているかの確認(f.closed)ではTrueが返ってきています。

閉じ忘れの心配もなくなるので、with文とopen関数はセットで使う方がいいでしょう。
エラーが出てしまった場合でも閉じてくれます。

文字エンコード

ファイルの文字エンコードと読み込むエンコードが異なるとエラーが出ます。

日本語を扱うときは特に注意が必要です。

ちょっと例示してみます。

ファイルのエンコードは「utf-8」。中身は「テストです」と書いています。
環境を変えてWindows10、Python3.8.2で開きます。

$ python
Python 3.8.2
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('test.txt') as f:
...     f.read()
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
UnicodeDecodeError: 'cp932' codec can't decode byte 0x86 in position 2: illegal multibyte sequence

エラーメッセージにある'cp932'というのは「Microsoftが独自にShift_JISを拡張したもの」です。
Windowsの環境であれば、デフォルトでそのエンコードを使って開こうとします。

そのため、ファイルのエンコード(utf-8)と異なり、エラーが出ました。
Macだとデフォルトで読み込むのがutf-8のため、この場合エラーは出ません

読み込むエンコードを指定してあげると解決できます。

open関数の引数に、encoding=''で指定します。

>>> with open('test.txt', encoding='utf-8') as f:  
...     f.read()
... 
'テストです'

ファイルの読み込み

これからの説明で使うファイルは共通して同じものを使用しました。

使用したファイル

numbers.txt

123456789
123456789
123456789
123456789
123456789

一行に1から9まで数字が書かれていて、それが5行あるテキストファイルです。

最後の行以外は改行しています。

すべて読み込む

ファイルのすべてを読み込むには、readメソッドを使います。

read()

引数にはなにも渡しません。

>>> with open('numbers.txt') as f:
...     text = f.read()
... 
>>> text
'123456789\n123456789\n123456789\n123456789\n123456789'
>>> print(text)
123456789
123456789
123456789
123456789
123456789

すべて読み込み、変数に入れた後、もうファイルには用がないのでwith文からは抜けています。

変数textには文字列が入っています。文字列中の「¥n」は改行が変換されたものです。

>>> len(text)
49

上記から文字列の改行をふくめた長さが、49文字だとわかります。

>>> text.split('\n')
['123456789', '123456789', '123456789', '123456789', '123456789']

splitメソッドを使い、引数に'¥n'を渡すと、一行ごとに分割することもできます。

特定の数値分を読み込む

read(数値)

readメソッドの引数に数値を渡すと、その数値分ファイルを読み込みます。

>>> with open('numbers.txt') as f:
...     text = f.read(20)
... 
>>> text
'123456789\n123456789\n'
>>> len(text)
20

改行を含めて20文字分読み込んでいます。

一行ずつ読み込む

一行ずつ読み込むには3つやり方があります。

メソッドを使うか、
for文にかけるか、
ファイルオブジェクトをリストに変換して利用します。

メソッドは、readlineを使います。

readline()

>>> f = open('numbers.txt') 
>>> f.readline()
'123456789\n'
>>> f.readline()
'123456789\n'
>>> f.readline()
'123456789\n'
>>> f.readline()
'123456789\n'
>>> f.readline()
'123456789'
>>> f.readline()
''
>>> f.close()

メソッドを実行するたびに一行ずつ読み込んでいます。

「''」はファイルの終端まで読み込んだことを表しています。
メソッドを繰り返したとしても空文字しか返ってきません。

つぎはfor文にかけてみます。
メモリと処理の効率が良い方法です。

ファイルから複数行を読み取るには、ファイルオブジェクトに対してループを書く方法があります。この方法はメモリを効率的に使え、高速で、簡潔なコードになります。

7.2.1. ファイルオブジェクトのメソッド [docs.python.org]
>>> with open('numbers.txt') as f:
...     for text in f:
...             text
... 
'123456789\n'
'123456789\n'
'123456789\n'
'123456789\n'
'123456789'

ファイルオブジェクトをfor文にかけました。改行区切りで取り出されます。

改行文字は、rstirpを使うと取り除くことができます。

>>> '123456789\n'.rstrip('\n')
'123456789'

最後はリストにしましょう。
リストにすることで一行ずつの処理ができます。

リスト化するには、ファイルオブジェクトをreadメソッドで読み込んで分割するか(これはすべて読み込むでやりました)、readlinesメソッドを使うか、list関数で変換します。

readlines()

list(ファイルオブジェクト)

>>> with open('numbers.txt') as f:
...     m_lines = f.readlines()
... 
>>> m_lines
['123456789\n', '123456789\n', '123456789\n', '123456789\n', '123456789']
>>> with open('numbers.txt') as f:
...     f_lines = list(f)
... 
>>> f_lines
['123456789\n', '123456789\n', '123456789\n', '123456789\n', '123456789']

メソッドと関数、どちらを使っても結果は同じです。

リストに変換すると何行あるか確認したり、ファイルの後ろから順に取り出したりできるようになります。

どちらも関数を使って実現しました。

# 行数確認
>>> len(m_lines)
5

# 逆から順に
>>> for text in reversed(m_lines):
...     text  
... 
'123456789'
'123456789\n'
'123456789\n'
'123456789\n'
'123456789\n'

ファイルの位置

ファイルオブジェクトは位置を記憶していて、読み込んだり書き込んだりするとその位置が変化します。

現在の位置を表示させてみましょう。
tellメソッドを使います。

tell()

以下のファイルを開きます。

abc.txt

abcdefghi
jklmnopqr
stuvwxyz

>>> f = open('abc.txt')
>>> f.tell()
0
>>> f.readline()
'abcdefghi\n'
>>> f.tell()
10

ファイルを開いた直後は、ファイルの先頭にいます。
位置は0です。

ファイルから一行読み込みました。
tellメソッドを使うと10が返ってきています。
一行読み込みこんでいるので(改行を入れて10文字分)、現在の位置は10です。

バイナリモードではファイルの先頭からのバイト数が返ってきます。

位置を移動する

seekメソッドを使って位置の移動ができます。

seek(値, 基準とする位置)

バイナリモードでは基準とする位置を、
0(ファイルの先頭)、
1(現在の位置)、
2(ファイル末尾)から選べます。

テキストモードでは基準とする位置を選べません。
常にファイルの先頭が基準となります。
※例外として、seek(0, 2) は使うことができ、ファイル末尾へ移動します。

この基準とする位置に、引数で与えた値を足した数移動します。

abcdefghi\n
1234567890
jklmnopqr\n
1234567890
stuvwxyz
12345678

>>> f = open('abc.txt')
>>> f.tell()
0
>>> f.seek(15) 
15
>>> f.readline()
'opqr\n'
>>> f.tell()
20
>>> f.seek(0)
0

seek(15)でアルファベット「n」の後ろに移動しました。

そこからまた一行読み込むと、位置が20になっています。

seek(0)で再び先頭に戻りました。

ファイルの書き込み

ファイルに書き込むメソッドは、writeメソッドwritelinesメソッドの2つです。

write(文字列)

writelines(リスト)

文字列のリストを書き込みたいときには、writelinesを使います。
writeline ではなく writelines です。

>>> lines = ['ハミガキ粉', '箱ティッシュ', '除菌グッズ', '入浴剤']
>>> with open('買い物リスト.txt', 'w+') as f:
...     f.writelines(lines)
...     f.write('目薬')
...     f.seek(0)
...     print(f.read())

... 
2
0
ハミガキ粉箱ティッシュ除菌グッズ入浴剤目薬

最初にwritelinesでリストを書き込み、つぎにwriteで「目薬」を追加しました。

2と0が返ってきていますが、2はwriteメソッドで書き込んだ文字数、0はseekメソッドで移動した位置が返ってきています。

書き込み時、どちらのメソッドも改行は自動で挿入されません。
必要なら末尾に改行文字を付けます。

>>> n_lines = [x + '\n' for x in lines]
>>> n_lines
['ハミガキ粉\n', '箱ティッシュ\n', '除菌グッズ\n', '入浴剤\n']
>>> with open('買い物リスト.txt', 'w+') as f:
...     f.writelines(n_lines)
...     f.write('目薬\n')
...     f.seek(0)
...     print(f.read())
... 
3
0
ハミガキ粉
箱ティッシュ
除菌グッズ
入浴剤
目薬
 

まとめ

ファイルの読み書きについて見てきました。

open(ファイルのパス, モード)で開く。
close()で閉じる
with文とopenを一緒に使うと良い。

読み込み
read() すべて

read(数値) 数値分
readline() 一行

ファイルオブジェクトをfor文にかける
readlinesメソッドでリスト化
list(ファイルオブジェクト)

tell()で位置の確認
seek(値, 基準点)で移動

書き込むメソッド
write(文字列)
writelines(リスト)

このページが少しでもお役に立てたのなら幸いです。

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