正規表現は特定の OS やプログラミング言語に縛られた機能ではない。もともとは 1950 年代に数学者スティーブン・クリーネが提唱した「正規言語」という理論に端を発する。その後、テキスト処理ツールやプログラミング言語に実装され、今日ではほぼすべての言語で使える汎用的なパターンマッチング記法となった。
正規表現はどこに属するのか
正規表現は OS の機能ではなく、各ツールや言語が独自に実装している。Linux の grep、sed、awk もそれぞれ微妙に異なる正規表現エンジンを持つ
Python は re モジュール、JavaScript は RegExp オブジェクト、Java は java.util.regex パッケージというように、各言語が独自の正規表現エンジンを提供している
つまり正規表現とは「パターンを記述するための共通言語」であり、その解釈と実行は各処理系が担う。基本的な記法(*、+、[] など)は共通しているが、細かい機能や書き方は実装によって異なる。
正規表現エンジンの種類
大きく分けて 2 つの実装方式がある。
grep や awk が採用。高速だが、後方参照などの高度な機能をサポートしない。
Python、Perl、JavaScript などが採用。バックトラッキングにより後方参照や先読みをサポートするが、パターンによっては遅くなる場合がある。
Python の re モジュールは NFA 方式を採用しており、\1 のような後方参照や (?=...) のような先読みが使える。
Python で「自分なりの正規表現」は作れるか
「独自の記法を定義して、それをパターンマッチに使いたい」という意味であれば、答えは「できるが、かなり大変」となる。
正規表現エンジンを自作するには、以下のステップが必要になる。
パターン文字列をトークンに分解(字句解析)
トークンから構文木を構築(構文解析)
構文木をオートマトンに変換
オートマトンで文字列をマッチング
これは言語処理系を作るのに近い作業だ。ただし、簡易的なものなら Python でも実装できる。たとえば「ワイルドカード * だけをサポートする簡易マッチャー」程度なら数十行で書ける。
def simple_match(pattern, text): """ 簡易ワイルドカードマッチャー * は任意の文字列にマッチ """ import re # * を正規表現の .* に変換 regex = pattern.replace("*", ".*") regex = f"^{regex}$" return bool(re.match(regex, text)) print(simple_match("hello*", "hello world")) # True print(simple_match("*world", "hello world")) # True print(simple_match("he*ld", "hello world")) # True print(simple_match("foo*", "hello world")) # False
この例では内部で Python の re モジュールを使っているが、独自記法を定義して既存エンジンに変換するというアプローチは現実的な選択肢だ。
本格的な正規表現エンジンを作るなら
Python で一から正規表現エンジンを実装したい場合、Russ Cox の記事 "Regular Expression Matching Can Be Simple And Fast" が優れた入門資料となる。NFA を使った実装方法が詳しく解説されている。
また、Python には regex というサードパーティライブラリがあり、標準の re モジュールより高機能な正規表現を使える。独自記法を作るよりも、既存の強力なエンジンを活用するほうが多くの場合は効率的だろう。