Pythonでファイル内の任意の文字列を含む行を抽出(grep的処理)
Pythonでテキストファイル内の任意の文字列を含む行を抽出する方法を説明する。いわゆるgrep的な処理。
- 行を抽出する基本的な流れ
- 行の中身を抽出
- 行番号を抽出
- 行番号と行の中身を抽出
- 該当する最初の行のみ抽出
行を抽出する基本的な流れ
テキストファイルから条件に応じて行を抽出する基本的な流れは以下の通り。
readlines()
で各行を要素とするリストを取得- リストから条件に応じた行を抽出
以下、具体的な例を示す。
テキストファイルの中身をリストとして取得
以下のファイルを例とする。
path = 'data/src/sample_for_grep.txt'
with open(path) as f:
print(f.read())
# XXX YYY ZZZ
# YYY
# aaa
# XXX
# ZZZ XXX
# xxx
ファイルへのリンクはこちら。
ファイルを開き、readlines()
で各行を要素とするリストを取得する。
with open(path) as f:
lines = f.readlines()
print(lines)
# ['XXX YYY ZZZ\n', 'YYY\n', 'aaa\n', 'XXX\n', 'ZZZ XXX\n', 'xxx']
print(type(lines))
# <class 'list'>
readlines()
で取得できるリストは行末の改行文字\n
を含んでいる。除去したい場合はリスト内包表記で各要素(各行)にstrip()
メソッドを適用する。
- 関連記事: Pythonリスト内包表記の使い方
lines_strip = [line.strip() for line in lines]
print(lines_strip)
# ['XXX YYY ZZZ', 'YYY', 'aaa', 'XXX', 'ZZZ XXX', 'xxx']
行の中身を抽出
各行を要素とするリストからリスト内包表記で条件を満たす行のみを抽出する。
l_XXX = [line for line in lines_strip if 'XXX' in line]
print(l_XXX)
# ['XXX YYY ZZZ', 'XXX', 'ZZZ XXX']
for
文で出力。
for line in l_XXX:
print(line)
# XXX YYY ZZZ
# XXX
# ZZZ XXX
条件を満たす最初の行・最後の行はインデックスを指定すれば取得できる。
print(l_XXX[0])
# XXX YYY ZZZ
print(l_XXX[-1])
# ZZZ XXX
なお、最初の行のみを抽出したい場合は、readlines()
でリストを取得する必要はない。後述。
様々な条件
in
だけでなく、文字列メソッドstartswith()
(特定の文字で始まる)、endswith()
(特定の文字で終わる)を使ったり、正規表現re.match()
を使ったりできる。
l_XXX_start = [line for line in lines_strip if line.startswith('XXX')]
print(l_XXX_start)
# ['XXX YYY ZZZ', 'XXX']
詳細は以下の記事を参照。
複数条件も可能。and
(かつ)、or
(または)で指定。
l_XXX_ZZZ_and = [line for line in lines_strip if ('XXX' in line) and ('ZZZ' in line)]
print(l_XXX_ZZZ_and)
# ['XXX YYY ZZZ', 'ZZZ XXX']
大文字小文字を区別せず抽出したい場合は、複数条件を指定するよりも、対象文字列を小文字(または大文字)に変換してから判定するほうが楽。
l_XXX_xxx = [line for line in lines_strip if 'xxx' in line.lower()]
print(l_XXX_xxx)
# ['XXX YYY ZZZ', 'XXX', 'ZZZ XXX', 'xxx']
行番号を抽出
条件を満たす行の行番号を抽出したい場合は組み込み関数enumerate()
を使う。
l_XXX_i = [i for i, line in enumerate(lines_strip) if 'XXX' in line]
print(l_XXX_i)
# [0, 3, 4]
条件の指定方法などは上の例と同じ。
行番号と行の中身を抽出
行番号と行の中身を合わせて抽出したい場合は以下のように書く。
l_XXX_both = [(i, line) for i, line in enumerate(lines_strip) if 'XXX' in line]
print(l_XXX_both)
# [(0, 'XXX YYY ZZZ'), (3, 'XXX'), (4, 'ZZZ XXX')]
行番号と中身を分割することも可能。
l_i, l_str = list(zip(*l_XXX_both))
print(l_i)
# (0, 3, 4)
print(l_str)
# ('XXX YYY ZZZ', 'XXX', 'ZZZ XXX')
該当する最初の行のみ抽出
条件に該当する最初の行だけを抽出したい場合は、これまでの例のようにreadlines()
ですべての行をリスト化する必要はない。
ファイルオブジェクトをそのままfor
文で回しファイルの先頭から一行ずつ文字列として取得し判定する。
with open(path) as f:
for i, line in enumerate(f):
if 'aaa' in line:
break
print(i)
# 2
print(line)
# aaa
末尾の改行文字\n
を含んだ文字列に対する条件を指定する必要があるので、特に一致==
を条件とする場合は注意。
with open(path) as f:
for i, line in enumerate(f):
if line == 'ZZZ XXX\n':
break
print(i)
# 4
print(line)
# ZZZ XXX
サイズが大きいファイルのヘッダーが何行目までかを確認したりするのに便利。