Developer

【Python独学】テキストファイルから行を抽出する
2021.03.31
Lv1

【Python独学】テキストファイルから行を抽出する

今回は、前回前々回と見てきたPythonによるテキストファイルの読み込みを利用して、
あるテキストファイル内から任意の文字列を含んだ行を抽出してみましょう。

  • 内容の確認
  • テキストファイルを読み込んで表示しよう
  • 特定の行を出力しよう
  • 文字列を入力しよう/ファイル名を入力しよう

内容の確認

Pythonにはテキストファイルを開き、内容を読み取るための関数・メソッドが用意されています。
それを利用し、今回はテキストファイルの内容から特定の文字列を含んだ行を抽出する
プログラムを書いてみましょう。
Linuxで言うところの、grep的なものを作ると思ってください。


テキストファイルを読み込んで表示しよう

では、前回までの内容を参考に、テキストファイルを読み込む部分から書いていきましょう。
というわけで、with~as構文とopen関数を使ってファイルを開きます。
なお、テスト用のファイルとして、Pythonプログラマが持つべき心構えをまとめたとされる
「The Zen of Python」を用意しました。

grep_sample.txt

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

なお、「The Zen of Python」はPythonインタプリタ上で以下のように入力すると表示することができます。

import this

それではサンプルファイルも用意したところで、ファイルを開いてみましょう。

with open("grep_sample.txt") as file:

with~as構文を使うことで、close関数を記述しなくてもプログラムが終了したタイミングで
必ずファイルを閉じてくれるようになります。
open関数の引数には、ファイルの絶対パスか相対パスを指定しましょう。
今回の例では、ファイルを.pyファイルと同じフォルダに配置した上で相対パスを指定しています。
次に、ファイルを1行ずつ読み込んで、表示する処理を書いてみましょう。

with open("grep_sample.txt") as file:

	lines = file.readlines()
	
	s_lines = [line.strip() for line in lines]

	for line in s_lines:
		print(line)
C:\Python>python 14-3_1.py
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

readlinesで読み取った行をリスト化します。
ただし、そのままリストを出力すると改行コードが各要素の末尾に含まれてしまうため、
3行目でstripメソッドを用いて改行コードを削除した新しいリストを作っています。

テキストファイルの内容を取得、出力することができました。
次に、この中からある特定の文字列を持つ行のみ出力してみたいと思います。

特定の行を出力しよう

特定の文字列を含む行を出力するには、リストの各要素に対してin演算子で判定をし、
条件を満たした行のみ出力するようにします。

with open("grep_sample.txt") as file:

	lines = file.readlines()
	
	s_lines = [line.strip() for line in lines]

	for line in s_lines:
		if "than" in line:
			print(line)
C:\Python>python 14-3_1.py
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Now is better than never.
Although never is often better than *right* now.

5行目の、for文の処理部分にてif文とin演算子を使って、リストから取り出した要素を判定します。
今回は仮の文字列として、”than”という文字列を指定しています。
取り出した要素、つまりテキストファイルから読み込んだ1行が指定した文字列を含んでいる場合、
判定がTrueになり、if文のprintが実行されます。
これで、指定した値でファイル内の任意の文字列を含む行を抽出することができました。

文字列を入力しよう/ファイル名を入力しよう

さて、プログラム内で指定した文字列を含む行を抽出することはできました。
ただし、このままではプログラム内で指定したファイルの、指定した文字列しか抽出することができません。
ですので、ファイル名と検索する文字列を入力できるようにしましょう。
実行したプログラムの中で、入力を受け付けてそれを変数として取得するにはinputを用います。

with open("grep_sample.txt") as file:

	lines = file.readlines()
	
	s_lines = [line.strip() for line in lines]

	input_value = input("検索文字列:")

	for line in s_lines:
		if input_value in line:
			print(line)
C:\Python>python 14-3.py
検索文字列:better            ### “better”を入力
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Now is better than never.
Although never is often better than *right* now.

実行結果の2行目で、”better”という文字を入力しています。
その結果、”better”という文字列を含む行のみ出力されているのがわかるかと思います。

同様に、検索対象のファイルも入力するようにしてみましょう。

file_path = input("検索ファイル:")

with open(file_path) as file:

	lines = file.readlines()
	
	s_lines = [line.strip() for line in lines]

	input_value = input("検索文字列:")

	for line in s_lines:
		if input_value in line:
			print(line)
C:\Python>python 14-3.py
検索ファイル:grep_sample.txt      ### ファイル名”grep_sample.txt”を入力
検索文字列:better            ### “better”を入力
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Now is better than never.
Although never is often better than *right* now.


まとめ

今回は、前回前々回で紹介したopen関数、with~as構文、readlinesメソッドを用いて
grepっぽいプログラムを作ってみました。
ファイルを開き、読み取る処理を用いることでこういったこともできます。
検索だけでなく、読み取った内容を元に別の処理を行ったりすることで、
テキストファイルを使った日常作業を補助するようなプログラムを書くこともできますので、ぜひやってみてください。


前回の確認問題の回答例

前回の記事はこちら→【Python連載】with~as構文を使ったファイルの操作

前回はこんな問題でした。

以下の文章の空欄を埋めてみましょう。
・open関数で取得したファイルは、()関数で閉じる必要がある。
・with~as構文を使うことで処理中に()が発生しても必ず最後にファイルを閉じることができる。
・open関数の引数のうち、modeのデフォルト値は()である。
・open関数の引数であるfileでは、開くファイルの()を指定する。

答えはこちらです。

・open関数で取得したファイルは、(close)関数で閉じる必要がある。
・with~as構文を使うことで処理中に(例外)が発生しても必ず最後にファイルを閉じることができる。
・open関数の引数のうち、modeのデフォルト値は(r)である。
・open関数の引数であるfileでは、開くファイルの(絶対パスもしくは相対パス)を指定する。

連載目次

独学で学ぶ Pythonプログラミング 連載目次