正芏衚珟はどう動いおいる゚ンゞンの仕組みを解説


正芏衚珟゚ンゞンの凊理フロヌ パタヌン: /a{2,3}b/ a = 文字 {2,3} = 量指定子 b = 文字 NFA状態遷移: S0 S1 S2 S3 S4 a a a? b b 受理状態 3文字目は省略可 入力 "aaab" のマッチング: Step 1 "a" → S0→S1 Step 2 "a" → S1→S2 Step 3 "a" → S2→S3 Step 4 "b" → S3→S4 ✓ ✓ マッチ成功: "aaab" は /a{2,3}b/ に適合
正芏衚珟 /a{2,3}b/ が入力文字列 "aaab" にマッチする過皋
ひよこ ひよこ

正芏衚珟っおよく聞くけど、そもそも䜕をするものなの

ペンギン先生 ペンギン先生

文字列のパタヌンを指定しお、怜玢・眮換・怜蚌ができる仕組みだよ。たずえば「メヌルアドレスっぜい文字列を探しお」みたいな曖昧な芁望を、`[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-z]+` ずいうパタヌンで正確に衚珟できるんだ。プログラミング蚀語のほずんどに暙準で搭茉されおいる、テキスト凊理の基本ツヌルだね。

ひよこ ひよこ

あの蚘号の矅列にそんな意味があったんだでも䞭ではどうやっおマッチを芋぀けおるの

ペンギン先生 ペンギン先生

正芏衚珟゚ンゞンの䞭栞は「ステヌトマシン状態機械」だよ。パタヌンを読み蟌むず、たず内郚的に状態の遷移図を䜜るんだ。たずえば `abc` ずいうパタヌンなら、「a を読んだら次の状態ぞ」「b を読んだら次ぞ」「c を読んだら䞀臎」ずいう3ステップの図になる。゚ンゞンは入力文字列を1文字ず぀読みながら、この図の䞊を移動しおいくんだよ。

ひよこ ひよこ

状態機械にも皮類があるっお聞いたこずがあるけど 

ペンギン先生 ペンギン先生

倧きく分けおNFA非決定性有限オヌトマトンずDFA決定性有限オヌトマトンの2皮類があるよ。DFAは各状態から次の状態が1぀に決たるので高速だけど、構築にメモリず時間がかかる。NFAは1぀の状態から耇数の遷移先がありえるので、分岐を詊しながら進む必芁がある。PythonやJavaScript、Rubyなど倚くの蚀語はNFAベヌスの゚ンゞンを䜿っおいお、その理由は埌方参照やキャプチャグルヌプなどの高機胜を実装しやすいからなんだ。

ひよこ ひよこ

NFAで分岐を詊すっお、具䜓的にはどうやるの

ペンギン先生 ペンギン先生

「バックトラッキング埌戻り」ずいう仕組みだよ。たずえば `a*b` ずいうパタヌンで文字列 `aaac` をマッチさせようずするず、゚ンゞンはたず `a*` で貪欲にaを党郚食べおしたう。でも次の `b` がcず䞀臎しないから、aを1぀吐き出しお再挑戊する。それでもダメならもう1぀吐き出しお ず埌戻りしながら党パタヌンを詊すんだ。マッチしない堎合は党郚詊しおから「䞍䞀臎」ず刀定するよ。

ひよこ ひよこ

党パタヌン詊すっおこずは、すごく遅くなるこずもあるの

ペンギン先生 ペンギン先生

そこがたさに正芏衚珟の萜ずし穎で、ReDoSRegular Expression Denial of Serviceず呌ばれる問題だよ。たずえば `(a+)+$` のようなパタヌンに `aaaaaaaaaaaaaaaaab` を䞎えるず、バックトラッキングの組み合わせが指数関数的に爆発しお、CPUが䜕分も固たるこずがある。これは実際に攻撃手法ずしお悪甚されるこずもあるんだ。

ひよこ ひよこ

こわっちなみに「貪欲」ず「怠惰」っおいうのもバックトラッキングに関係あるの

ペンギン先生 ペンギン先生

いいずころに気づいたね。`*` や `+` はデフォルトで「貪欲greedy」、぀たりできるだけ倚くの文字をマッチしようずする。`*?` や `+?` のように `?` を付けるず「怠惰lazy」になっお、できるだけ少なくマッチしようずするんだ。たずえばHTMLから `<b>倪字</b>` を抜き出すずき、`<.*>` だず貪欲に党䜓をマッチするけど、`<.*?>` なら最初の `<b>` だけにマッチする。ただし怠惰にしおもバックトラッキング自䜓は発生するから、ReDoS察策にはならないよ。

ひよこ ひよこ

メヌルアドレスずか電話番号のチェックにも正芏衚珟をよく䜿うよねあれっおどうなっおるの

ペンギン先生 ペンギン先生

よくある䟋を分解しおみよう。電話番号の `^0\d{1,4}-\d{1,4}-\d{4}$` は、「0で始たり、数字が1〜4桁、ハむフン、数字が1〜4桁、ハむフン、数字4桁で終わる」ずいう構造だね。メヌルアドレスはRFC準拠にするず恐ろしく耇雑になるから、実務では `^[^@]+@[^@]+\.[^@]+$` くらいのゆるいチェックにしお、あずは実際にメヌルを送っお確認するのがベストプラクティスだよ。正芏衚珟だけで完璧なバリデヌションをしようずするず沌にハマるんだ。

ひよこ ひよこ

先読みずか埌読みっおいう機胜もあるっお聞いたけど、あれは䜕

ペンギン先生 ペンギン先生

先読みlookaheadず埌読みlookbehindは「マッチはするけど消費しない」特殊な条件だよ。たずえば `\d+(?=円)` は「円」の前にある数字にマッチするけど、マッチ結果に「円」は含たれない。パスワヌドの匷床チェックで「英倧文字・小文字・数字をすべお含む」みたいな耇合条件を1぀の正芏衚珟で曞くずきに重宝するんだ。`(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}` のように先読みを䞊べるず、AND条件ずしお機胜するよ。

ひよこ ひよこ

䟿利だけど、正芏衚珟を䜿わないほうがいい堎面っおあるの

ペンギン先生 ペンギン先生

たくさんあるよ。HTMLやXMLのパヌスは正芏衚珟だけでは正確にできないし、JSONやCSVにも専甚パヌサヌを䜿うべきだね。あず固定文字列の怜玢なら `indexOf` や `includes` のほうが圧倒的に速い。正芏衚珟が埗意なのは「パタヌンが可倉で、か぀テキストが構造化されおいない」ケヌスだよ。ログファむルから特定の゚ラヌパタヌンを抜出するずか、ナヌザヌ入力の簡易バリデヌションずか。䞇胜に芋えお、䜿いどころを遞ぶツヌルなんだ。

ひよこ ひよこ

実際に正芏衚珟のせいで倧事故になったケヌスっおあるの

ペンギン先生 ペンギン先生

有名なのは2019幎のCloudflareの障害だね。WAFWebアプリケヌションファむアりォヌルのルヌルに远加された正芏衚珟にカタストロフィック・バックトラッキングが発生しお、䞖界䞭のCloudflare利甚サむトが玄27分間ダりンしたんだ。原因は `(?:(?:\")+)+` のようなネストした量指定子で、入力によっおは指数関数的にCPUを消費するパタヌンだった。この事件以降、Cloudflareはバックトラッキングしない゚ンゞンRustのregexクレヌト等ぞの移行を進めたよ。

ひよこ ひよこ

䞖界芏暡の障害が正芏衚珟1぀で 察策ずしおはどうすればいいの

ペンギン先生 ペンギン先生

たず「ネストした量指定子」を避けるこず。`(a+)+` や `(a*)*` のようなパタヌンは危険信号だよ。次に、アトミックグルヌプ `(?>...)` や所有量指定子 `*+` を䜿える゚ンゞンならバックトラッキングを制埡できる。さらに根本的な察策ずしおは、RE2やRustのregexクレヌトのようにバックトラッキングしない゚ンゞンを遞ぶ方法がある。これらはNFAではなくDFAベヌスで動くから、どんな入力でも線圢時間で凊理が完了するんだ。セキュリティが重芁なシステムでは、゚ンゞン遞定から考えるのが珟代のベストプラクティスだね。