テキストファイルから特定の文字列が入った行を抽出する

はじめに

仕事でいくつかテーマがあって、そのPentahoETL処理のログから、目的のテーマのログを抽出する必要があった。

Pythonで実現予定なので、記事を書いていく。

テキストファイルの用意

ChatGPTにお願いして、1000行のなかにJapanが含まれるテキストファイルを生成してもらった。

テキストはこちら。japanが10個ランダムと指示したはずだが、64個入っていた。

row_filter/random_countries.txt at master · ki-hi-ro/row_filter

ソースコードの作成

こちらもGPTを活用した。生成AIはどんどん活用していきたい。

ホリエモンがYouTubeの広告で「AIは大脳の機能拡張、アシストしてくれるからどんどん使った方がいいんだよ」と話していた。

import sys

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

row_filter/filter_japan.py at master · ki-hi-ro/row_filter

ソースコードの実行

以下の太字のコマンドを実行した。

(.venv) hiroki@shibatahiroshitakanoiMac row_filter % python filter_japan.py ran
dom_countries.txt filter_japan.txt
Filtered lines with 'Japan' saved to filter_japan.txt

実行後に生成されたファイルはこちら。

row_filter/filter_japan.txt at master · ki-hi-ro/row_filter

ソースコードの解説

まずは、sysモジュールをインポートすることで、コマンドライン引数(sys.argv)を使えるようにする。

import sys

filter_japan関数について見ていこう。

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

input_fileを読み込みモード、output_fileを書き込みモードで開いている。with文を用いているので、処理が終わったらファイルが自動で閉じる。

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

for文でinput_fileの各行を順番に読み取る。

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

strip()前後の余白を削除した文字列に「Japan」が含まれているか判定する。

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

Japanが含まれていたら、output_fileに書き込む。

def filter_japan(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
        for line in infile:
            if "Japan" in line.strip():
                outfile.write(line)

次にメイン処理について解説していく。

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

こちらはスクリプトが直接実行された場合にのみ実行されるブロック。

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

sys.argvの長さを確認し、必要な2つの引数(input_file, output_file)と、Pythonのファイル名と合わせて3つにならなかったら、使い方のメッセージを表示して、スクリプトを終了する。

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

インプットとアウトプットそれぞれのファイルパスを取得する。

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

filter_japan関数を呼び出し、フィルタ処理を実行する。そして、終了のメッセージを出力する。

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python filter_japan.py <input_file> <output_file>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    filter_japan(input_file, output_file)
    print(f"Filtered lines with 'Japan' saved to {output_file}")

sys.argvの中身

デバッグしてみる。

VSCodeのfn + F5キーで以下の画面を開き、Python Debuggerを選択。

コマンドライン引数を入力できる状態の選択肢を先ほど選んだので以下の画面になった。

pythonファイル名、Japanを含む行のみを抽出したいテキストファイル名、抽出後のテキストファイル名(任意)を半角空白のスペースを開けて入力する。

実行前にブレークポイントを以下のように設定してあった。

実行後にデバッグコンソールに「sys.argv」を入力する。

すでに、pythonファイルのパスがあったため、4つ値が入ってしまっていた。

プログラミングでは、こういうことがよくある。

コンピューターは「おばかで従順」なので、よしなにやってくれるわけではない。

デバッグの場合は、pythonファイルを指定する必要はないので、コマンドライン引数には、読み込みファイルと出力ファイルのみを指定しよう。

これでデバッグを実行すると、値が3つであることが確認できた。

ステップインしていくと、input_fileとoutput_fileに指定した値が代入されていることが確認できた。

sys.argvのargvって何?

argument vector(引数のベクトル)の略語。

ベクトルは配列という意味。

先ほど確認したように、スクリプト実行時のコマンドライン引数をリストとして格納している。

C言語由来の概念。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です