BNFを書いてがっつり実装する場合は Lark などを使いますが,このエントリでは簡便な方法として pyparsing モジュールの使い方を紹介します.
ログファイルから特定のデータだけを抜き出す,といった用途ならpyparsingで十分です.
pyparsing の使い方
サンプル
とりあえず以下のログファイルを読み込むと
code=200 msg=OK code=401 msg=NotFound Error: no such file.
以下の情報を取り出すコードを目標とします
- (200, "OK")
- (401, "NotFound")
が,話を簡単にするために,ひとまず
”code=200 msg=OK”から(200,"OK")を取り出すコードを書くと
次のようになります
import pyparsing as pp from pyparsing import pyparsing_unicode as uni token_string = pp.Combine(pp.Word(uni.printables), adjacent=False, joinString=" ") token_int = pp.Word(pp.nums) token_float = pp.Combine(pp.Optional('-') + pp.Word(pp.nums) + '.' + pp.Word(pp.nums)) syntax = "code=" + token_int("code_number") + "msg=" + token_string("msg_text") result = syntax.search_string("code=200 msg=OK") for e in result: print(list(e.items()))
実行すると
[('code_number', '200'), ('msg_text', 'OK')]
という結果になります
説明
モジュールの読み込み
import pyparsing as pp from pyparsing import pyparsing_unicode as uni
トークンの定義
データに対応する文字列をトークンとして定義します
- 文字列のデータに対して token_sring
- 整数のデータに対して token_int
- 浮動小数点のデータに対して token_float
を定義しています
token_string = pp.Combine(pp.Word(uni.printables), adjacent=False, joinString=" ") token_int = pp.Word(pp.nums) token_float = pp.Combine(pp.Optional('-') + pp.Word(pp.nums) + '.' + pp.Word(pp.nums))
式の定義
抽出したい行は
code=整数値 msg=文字列
の形になります
また読み取った整数値には code_number,
文字列には msg_text という変数名をつけることにします
これをpythonでかくと
syntax = "code=" + token_int("code_number") + "msg=" + token_string("msg_text")
となります
構文解析の実行
result = syntax.search_string("code=200 msg=OK"") for e in result: print(list(e.items()))
実行結果は
[('code_number', '200'), ('msg_text', 'OK')]
となり,200とOKが抽出できていることがわかります
最終的なコード
import pyparsing as pp from pyparsing import pyparsing_unicode as uni token_string = pp.Combine(pp.Word(uni.printables), adjacent=False, joinString=" ") token_int = pp.Word(pp.nums) token_float = pp.Combine(pp.Optional('-') + pp.Word(pp.nums) + '.' + pp.Word(pp.nums)) syntax = "code=" + token_int("code_number") + "msg=" + token_string("msg_text") data = """ code=200 msg=OK code=401 msg=NotFound Error: no such file. """ for text in data.split("\n"): result = syntax.search_string(text) for e in result: print(list(e.items()))
結果は
[('code_number', '200'), ('msg_text', 'OK')] [('code_number', '401'), ('msg_text', 'NotFound')]
データが多い時はpandasのDataFrameに突っ込むと使い勝手がいいです
import pandas as pd tmp = [] for text in data.split("\n"): result = syntax.search_string(text) for e in result: tmp.append(e.items()) df = pd.DataFrame(tmp) display(df)
これで結果を表形式で表示できます