Python 指定文字列をファイル内で検出して開始行と終了行を取得する。そして、その区間の複数行を抽出する。正規表現「re」

'20/09/12更新:雛形コードの汎用性を高めました。
 文字列を検出するには、正規表現reモジュールを使用します。
・先頭文字列をマッチさせたい場合は re.match()
・文字列の一部を部分マッチさせたい場合は re.search()
 本コードの実施例を示します。下図は抽出対象のテキストファイルです。

f:id:HK29:20200322130045p:plain

 下図は、本コードを実行して生成したテキストファイルです。検出したい文字列をリストで['NAME ID', 'D']と指定するだけで、その開始文字列と終了文字列の行を抽出して、その行区間のデータを新たなテキストファイルへ書き出します。

f:id:HK29:20200912104952p:plain

 その他の指定方法による結果例が次の3つです。一番左図は、「matrix」を最初に見つけた段階で抽出して空白行までの複数行を抽出します。中央図は、「matrix」とその次の行に「ID」がある場合に抽出しています。一番右図は、直接行番号「19」と「21」を指定することで抽出しています。これらはクラスを用いて、メソッドを呼び出すことで実現しています。

f:id:HK29:20200322130200p:plain

▼本プログラム

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import re
import pandas as pd

class Extract_data_from_file():
    def __init__(self, input_file):
        self.input_file = input_file
        print('reading file -> ', self.input_file)

    # データ抽出方法A:最初に指定した文字列とヒットした場合を検出して、複数行データを抽出したい場合
    def extract_targetrows_method_A(self, target_list):
        with open(self.input_file, "r") as f:
            row_start = ""
            row_end = ""

            if len(target_list) == 2:
                for i, row in enumerate(f, start=1):
                    print(row)
                    # ターゲットリスト一つ目にマッチした場合に、その行を一旦メモる
                    if re.match(target_list[0], row): #先頭文字がマッチの場合はmatch
                        row_start = i
                    # ターゲットリスト二つ目にマッチした場合
                    if row_start != '' and re.match(target_list[1], row):
                        row_end = i
                        break
                    # 抽出開始行であるrow_startが空でなく、現在の行が改行コードのみの場合
                    if row_start != "" and row=="\n":
                        row_end = i
                        break
            else:
                print("target_list must be max 2 :" + str(target_list))
                sys.exit()
            
        if row_end == "":
            print("target sentence " + str(target_list) + " does not exist")
            sys.exit()
        print(row_start, row_end)

        return (row_start, row_end)


    # データ抽出方法B:(自分がやりたいような指定条件でヒットした場合を検出して、複数行データを抽出したい場合
    def extract_targetrows_method_B(self, target_list):
        with open(self.input_file, "r") as f:
            row_start = ""
            row_end = ""
            if len(target_list) == 2:
                for i, row in enumerate(f, start=1):
                    # ターゲットリスト一つ目にマッチした場合に、その行を一旦メモる
                    if re.match(target_list[0], row): #先頭文字がマッチの場合はmatch
                        buf_row = i
                    # ターゲットリスト二つ目にマッチ且つ、その行がbuf_row の次の行(+1)である場合
                    if re.search(target_list[1], row) and (i == buf_row + 1): # #文中がマッチの場合はsearch
                        row_start = buf_row
                    # 抽出開始行であるrow_startが空でなく、現在の行が改行コードのみの場合
                    if row_start != "" and row=="\n":
                        row_end = i
                        break
            elif len(target_list) == 1:
                for i, row in enumerate(f, start=1):
                    # ターゲットリスト一つ目にマッチした場合に、それが開始行
                    if re.match(target_list[0], row): #先頭文字がマッチの場合はmatch
                        row_start = i
                    # 抽出開始行であるrow_startが空でなく、現在の行が改行コードのみの場合
                    if row_start != "" and row=="\n":
                        row_end = i
                        break
            else:
                print("num of target_list is max 2 :" + str(target_list))
                sys.exit()
            
        if row_end == "":
            print("target sentence " + str(target_list) + " does not exist")
            sys.exit()
        print(row_start, row_end)

        return (row_start, row_end)

    
    # 抽出した複数行データをテキストファイルへ書き出す
    def write_data(self, extract_row_list, output_file):
        with open(self.input_file, "r") as f:
            buf = []
            for i, row in enumerate(f):
                if ( i >= int(extract_row_list[0])) and (i < int(extract_row_list[1])):
                    buf.append(row)

        with open(output_file, "w") as f:
            f.writelines(buf)


def main():
    # オブジェクト化
    myobject = Extract_data_from_file(input_file)
    
    # データ抽出1:Aの方法でターゲットリスト1の区間を抽出する場合
    row_list_A1 = myobject.extract_targetrows_method_A(target_list_1)
    start_row, end_row = row_list_A1 # アンパック代入
    myobject.write_data((start_row - 1, end_row + 1), 'extract_data_A_1.txt')
    
    # データ抽出2:Bの方法でターゲットリスト2の区間を抽出する場合
    row_list_B1 = myobject.extract_targetrows_method_B(target_list_2)
    myobject.write_data(row_list_B1, 'extract_data_B_2.txt')

    # データ抽出3:Bの方法でターゲットリスト3の区間を抽出する場合
    row_list_B2 = myobject.extract_targetrows_method_B(target_list_3)
    myobject.write_data(row_list_B2, 'extract_data_B_3.txt')

    # データ抽出4:予め、抽出したい行区間が決まっていてその区間を抽出する場合
    myobject.write_data([19, 21], 'extract_data_4.txt')
    
    
if __name__ == '__main__':
    input_file = "raw_data.txt"

    target_list_1 = ['NAME ID', 'D']
    target_list_2 = ['matrix']
    target_list_3 = ['matrix', 'ID']
    
    main()
    print('finished')

以上

<広告>