[Python] 任意の文字や単語を含む行だけを簡単に出力する方法

Python
この記事は約10分で読めます。
強気な男の子
強気な男の子

お前の記事読んだけど、ファイルコピーしてるだけじゃん。
ふつ~にファイルコピーした方がはえ~し、意味なくね?

強気な子供が苦手なモグラ
強気な子供が苦手なモグラ

ここまでは環境づくりです。これから色々な事が出来るようになるのデス!

上の記事を参考にすれば、既存のテキストファイルを開いて、新しいファイルを作成し、既存ファイルの中身をそのまま書き込む事が出来るようになると思います。

これで読み込んだファイルを色々と編集してから出力する準備は出来ました。
次のステップとして、これから紹介するテキストファイルとPythonソースをベースに、入力ファイルを色々と加工してみましょう。

本記事に記載のPythonスクリプト動作確認環境

OS : Windows10(64bit版)
Python version : 3.7.3

スポンサーリンク

任意の文字や単語を含む行だけを出力するベースのソースコード

ベースとなるテキストファイルとPythonソースコードおよび実行結果
テキストファイル
ベースとなるPythonソースコード
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")
for line in f:
    print (line,end="")
実行結果(標準出力)

出席番号,名前,点数
1,たろう,96
2,じろう,68
3,さぶろう,31

ベースとなるPythonソースコードの途中に条件文を追加する事で、処理結果を色々と変える事が出来るようになります。

ある単語を含む行のみを出力する・しない

ベースソースに1行、条件文を追記すればOKです。print()関数を実行する行のインデントを下げる事を忘れないように注意してください。

たろう” を含む行だけを抜き出す
Pythonソース
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")
for line in f:
    if ("たろう" in line):
        print (line,end="")
実行結果

1,たろう,96

ソースの2行目で “たろう” を含むか含まないかの判定をしています。if文()中の条件式(*1)が成立(真:True)であれば、ifブロック(*2)内の print (line,end=””) を実行する、不成立(偽:False)であれば、実行しないとなります。

  1. 条件式:成立するかしないか(TrueFalseか)を返す式です。このような2値だけを持つものを、bool値と呼びます。
  2. ブロック:if分やfor文といった条件文のカッコ中に示した条件式が成立した時に実行するコードの範囲をコードブロック、またはブロックと呼びます。ブロック範囲の示し方は言語によって色々ですが、Pythonではその範囲をインデントで示します。

pythonの比較演算子 「in」

この 比較演算子 「inはとても便利で、私も好んでよく使っています。
ここでの例は文字列が対象ですが、list 型やタプル型、辞書型にも使えます。

使い方は、ざっくりこんな感じ。
検査する値” in “検索対象

ちなみにコーディングしている途中であまり自信が無い行は、Pythonをインタラクティブモードで起動して、その1行だけを試す事が出来ます。

>>> "たろう" in "1,たろう,96"
True
>>> "たろう" in "2,じろう,68"
False

pythonの比較演算子 「not in」

逆に “たろう” だけを表示したくない場合は、 比較演算子「not in を使えば良いです。

>>> "たろう" not in "1,たろう,96"
False
>>> "たろう" not in "2,じろう,68"
True

Pythonソース3行目を
 if (“たろう” not in line):
とした場合の実行結果は以下となります。

実行結果

出席番号,名前,点数
2,じろう,68
3,さぶろう,31

ファイルの1行目は必ず出力してから任意の文字を抜き出す方法

このテキストファイルは1行目に項目名が入っているので、必ず出力したくなるかと思います。

やりかたとして簡単に思いつくのは、

  1. ラインカウンタを持たせて、2行目以上になったら条件判定をする。
  2. ファイルのシーク位置をずらしてからfor文で回す。

となります。

ラインカウンタを持たせて、2行目以上になったら条件判定をする。

Pytthonソース
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")
counter=1
for line in f:
    if ( counter==1 ) :
        print (line,end="")
    elif ("じろう" in line):
        print (line,end="")
    counter=counter+1

まず2行目でカウンタとその初期値を「counter=1」として用意します。
次に4行目と5行目で、ファイルが1行目(counter==1)の場合は必ず出力としています。

最後に8行目でカウンタ値をプラス1しています。
もしこの行がなければ「カウンタ値はずっと1のまま -> for文を抜けるまで 5行目が必ず実行されつづける -> ファイルの中身を全て出力する」といった処理となります。

ちなみに8行目は 「i+=1」と表現する事も出来ます。こちらの方が「お、プログラムに慣れてるな」という印象を与えるので、慣れて来たらこちらを試してみてください。コードもコンパクトになるので、後で自分も見直しやすいかと思います。

実行結果

出席番号,名前,点数
2,じろう,68

ファイルのシーク位置をずらしてからfor文で回す

ファイルを操作する時は、シーク位置を意識するとより柔軟なプログラムを記述出来るようになります。シーク位置というのは、今現在ファイル全体の何処を指しているのか?という事です。

たとえばファイルを開いた直後のシーク位置は、ファイルの先頭である事が多いでしょう。ただしファイルを開く時に追記のモード引数 “a” を与えた場合、シーク位置はファイルの末尾となります。先頭だと上書きしちゃいますもんね。。

ちなみに
 for line in f:
の行が上手く動作するのは、シーク位置が1行づつずれていっているからです。

ここまで見ると、ファイルを1行だけ読みだしてシーク位置を1行だけずらしてから、for文でファイルオブジェクトfへアクセスすれば、上手く動きそうな気がしてきますね。ここで先日紹介したファイルオブジェクトのメソッド readline() を使用します。

Pytthonソース
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")
line = f.readline()
print (line,end="")
for line in f:
  if ("じろう" in line):
        print (line,end="")

2行目でファイルオブジェクトfにreadline() メソッドでアクセスする事で、ファイル1行目の文字列を取り出しています。この時、ファイルのシーク位置は1行移動します。

3行目は、ファイル1行目を代入した変数lineをprint()関数で出力しているだけです。

この実装方法では、動作を制御するカウンタを用意する必要はありません。

実行結果

出席番号,名前,点数
2,じろう,68

ファイルオブジェクトのメソッドreadlines()を使う方法

読み込むファイルサイズが大きくないのであれば、この方法が一番直感的かもしれません。

list型をそのままfor文で回す

Pythonソース
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")
lines = f.readlines()
print (lines[0],end="")
for line in lines[1:]:
  if ("じろう" in line):
        print (line,end="")
実行結果

出席番号,名前,点数
2,じろう,68

2行目でファイルオブジェクトfにreadlines()メソッドでアクセスする事で、ファイルの全行をlist型変数としてlinesへ代入しています。この代入された時に、linesはリスト型となります。

lines[0]にはファイルの1行目、lines[1]にはファイルの2行目・・・というように、ファイルの全行が一気に格納されます。

3行目でファイルの1行目を出力します。
4行目で、ファイルの2行目から最終行までをfor文で回します。lines[1:]という表記は、リストの要素1(ここではファイルの2行目)から最後までを示しています。省略する事で最後までとなります。

lines[1:3]とすれば要素1から3まで、 lines[:3] とすれば 要素0から3まで(lines[0:3]と同じ)、lines[:]とすれば全要素となりますが、これは lines と同じ意味となります。

list型の始まりが0なのは当たり前ですが、終わりが何処なのかは調べないと分からないので、慣れないと面喰いますが、lines[1:] といった省略表記はとても便利です。

list型の要素数を調べて、その回数だけfor文で回す

Pythonソース
f = open(r"C:\Python_source\01_TextFileRead\SampleTextSJIS.csv",encoding="shift_jis")<br>
lines = f.readlines()
for idx in range(len(lines)):
  if (idx==0):
      print (lines[idx],end="")
  elif ("じろう" in lines[idx]):
      print (lines[idx],end="")
実行結果

出席番号,名前,点数
2,じろう,68

3行目がぱっと見、1番訳が分からないかと思いますが、分かってしまえばとても簡単です。

len()はPythonの組み込み関数で、色々な型の要素数を返してくれます。len(lines)とすると、linesは4つの要素を含むリスト(元は4行のファイル)なので、4が返されます。

次にrange(len(lines))ですが、上の説明から実際はrange(4)となります。range()はfor文と組みわせる事で簡単にループを作る事が出来るのですが、ここではrange(4)で4回のループが作成されます。そしてループの先頭では、idxに0,1,2,3が代入されます。

まとめ

任意の文字や単語を含む行を出力するだけでしたが、色々なやり方があるんだなというのが伝わったかと思います。他にもやり方は色々とあるので、自分に合ったコードの実装を是非探してみてください。

この記事で示した方法はprint関数での標準出力ですが、このprint関数をファイルオブジェクトのwrite()メソッドに置き換える事で、ファイル出力とする事は簡単です。

最後に今回は単純な文字のマッチングでしたが、もっと厳格かつ柔軟にテキストを処理したい場合には、正規表現(regular expression)を用いることで実現が可能となります。

正規表現は言語に依存しない汎用的な記法なので、テキストの編集する機会が多い方は是非勉強される事をお勧めします。このブログでもいつか紹介したいと考えています。

それでは今日はこの辺で。

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