クイックスタート
チュートリアル
ツールと言語
リファレンス
書評
正規表現チュートリアル
はじめに
目次
特殊文字
非印刷文字
正規表現エンジンの内部動作
文字クラス
文字クラスの減算
文字クラスの積集合
文字クラスの省略形
ドット
アンカー
単語境界
オルタネーション
オプション項目
繰り返し
グループ化とキャプチャ
バックリファレンス
バックリファレンス、パート2
名前付きグループ
相対バックリファレンス
ブランチリセットグループ
フリースペースとコメント
Unicode
モード修飾子
アトミックグループ化
所有格量子化子
先行アサーションと後行アサーション
ルックアラウンド、パート2
マッチからテキストを除外する
条件式
バランスグループ
再帰
サブルーチン
無限再帰
再帰と量子化子
再帰とキャプチャ
再帰とバックリファレンス
再帰とバックトラッキング
POSIXブラケット式
ゼロ長マッチ
マッチの継続
このサイトについて、その他
はじめに
正規表現 クイックスタート
正規表現 チュートリアル
置換文字列 チュートリアル
アプリケーションと言語
正規表現 例
正規表現 リファレンス
置換文字列 リファレンス
書評
印刷可能なPDF
このサイトについて
RSSフィードとブログ
RegexBuddy—Better than a regular expression tutorial!

再帰レベルを指定するバックリファレンス

このチュートリアルの以前のトピックでは、正規表現の再帰正規表現のサブルーチンについて説明しました。このトピックでは、「再帰」という単語は、正規表現全体の再帰、キャプチャグループの再帰、およびキャプチャグループへのサブルーチン呼び出しを指します。前のトピックでは、これらの機能がRubyではPerlやPCREとは異なる方法でキャプチャグループを処理することも説明しました。

Perl、PCRE、およびBoostは、再帰から抜けたときにキャプチャグループを復元します。つまり、Perl、PCRE、およびBoostのバックリファレンスは、同じ再帰レベルでキャプチャグループによってマッチしたのと同じテキストにマッチします。これにより、回文のマッチングなどを行うことが可能になります。

Rubyは、再帰から抜けたときにキャプチャグループを復元しません。通常のバックリファレンスは、キャプチャグループが同じ再帰レベルか異なる再帰レベルでマッチを見つけたかに関係なく、バックトラックされなかったキャプチャグループの最新のマッチと同じテキストにマッチします。基本的に、Rubyの通常のバックリファレンスは、再帰に何の注意も払いません。

しかし、Rubyの通常のキャプチャグループの格納は再帰に対して特別な処理を受けませんが、Rubyは実際には、すべての再帰レベルで各キャプチャグループのマッチの完全なスタックを格納します。このスタックには、正規表現エンジンが既に抜けた再帰レベルも含まれています。

Rubyのバックリファレンスは、バックリファレンスが評価される再帰レベルに対して相対的な任意の再帰レベルでキャプチャグループによってマッチしたのと同じテキストにマッチできます。名前付きバックリファレンスと同じ構文で、名前に符号と数字を追加することでこれを行うことができます。ほとんどの場合、次を使用します。+0同じ再帰レベルのキャプチャグループのテキストを再利用したいことを指定するために使用します。正の数を指定して、より深い再帰レベルのキャプチャグループを参照できます。これは、正規表現エンジンが既に抜けた再帰になります。負の数を指定して、浅いレベルのキャプチャグループを参照できます。これは、まだ進行中の再帰になります。

JGsoft V2も、Rubyと同じ構文を使用して再帰レベルを指定するバックリファレンスをサポートしています。RubyとJGsoft V2で同じ動作を得るには、Rubyの\g構文をサブルーチン呼び出しに使用しなければなりません。

Rubyにおける奇数長の回文

Rubyでは、次を使用できます。\b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z])\b次のような回文単語にマッチします。a, dad, radar, racecar、そしてredividerこの例を単純にするために、この正規表現は文字数が奇数の回文単語のみにマッチします。

この正規表現がどのようにradarにマッチするかを見てみましょう。単語境界\bは文字列の先頭でマッチします。正規表現エンジンは「word」キャプチャグループに入ります。[a-z]rにマッチし、これは再帰レベル0の「letter」キャプチャグループのスタックに格納されます。ここで、正規表現エンジンは「word」グループの最初の再帰に入ります。(?'letter'[a-z])は再帰レベル1でマッチしてキャプチャします。a正規表現は「word」グループの2回目の再帰に入ります。(?'letter'[a-z])は再帰レベル2でdをキャプチャします。次の2回の再帰中に、グループはレベル3と4でarをキャプチャします。5回目の再帰は、[a-z]にマッチする文字列に残りの文字がないため失敗します。正規表現エンジンはバックトラックする必要があります。

正規表現エンジンは、「word」グループ内の2番目の選択肢を試す必要があります。正規表現の2番目の[a-z]は文字列の最後のrにマッチします。エンジンはこれで成功した再帰から抜けて、レベルを一つ戻って3回目の再帰に戻ります。

にマッチした後、\g'word'に到達すると、エンジンは\k'letter+0'に到達します。正規表現エンジンは既に文字列の終わりに到達しているので、バックリファレンスは失敗します。そのため、さらにバックトラックします。2番目の選択肢はこれでaにマッチします。正規表現エンジンは3回目の再帰から抜けます。

正規表現エンジンは再び\g'word'にマッチし、バックリファレンスを再度試行する必要があります。バックリファレンスは+0、つまり現在の再帰レベル2を指定します。このレベルでは、キャプチャグループはdにマッチしました。文字列の次の文字がrなので、バックリファレンスは失敗します。再びバックトラックすると、2番目の選択肢はd.

にマッチします。ここで、\k'letter+0'は文字列の2番目のaにマッチします。これは、正規表現エンジンが最初の再帰に戻っており、その際にキャプチャグループが最初のaにマッチしたためです。正規表現エンジンは最初の再帰から抜けます。

正規表現エンジンはこれですべての再帰の外に戻ります。このレベルでは、キャプチャグループはrを格納していました。バックリファレンスはこれで文字列の最後のrにマッチできます。エンジンはもう再帰内にはないので、グループの後の正規表現の残りの部分に進みます。\bは文字列の終わりでマッチします。正規表現の終わりに到達し、radarが全体のマッチとして返されます。

他の再帰レベルへのバックリファレンス

他の再帰レベルへのバックリファレンスは、回文の例を変更すれば簡単に理解できます。abcdefedcbaも、前の正規表現によってマッチする回文です。次の正規表現を考えてみましょう。\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-1'|z)|[a-z])\bバックリファレンスはこれで、キャプチャグループのスタックで1レベル浅いテキストにマッチしようとしています。これは文字zとオルタネーションされているため、バックリファレンスがマッチに失敗した場合でも何かをマッチさせることができます。

新しい正規表現は次のようなものにマッチします。abcdefdcbaz大量のマッチとバックトラッキングの後、2番目の[a-z]f正規表現エンジンは成功した5回目の再帰から抜ける。キャプチャグループ「letter」は、再帰レベル0~4でマッチa, b, c, d、そしてeを格納している。そのグループによるその他のマッチはバックトラックされ、そのため保持されていない。

ここで、エンジンはバックリファレンス\k'letter-1'を評価します。現在のレベルは4で、バックリファレンスは-1を指定します。そのため、エンジンはdにマッチしようとしますが、成功します。エンジンは4回目の再帰から抜けます。

バックリファレンスは、正規表現エンジンが最初の再帰から抜けるまでc, b、そしてaにマッチし続けます。ここで、すべての再帰の外で、正規表現エンジンは再び\k'letter-1'に到達します。現在のレベルは0で、バックリファレンスは-1を指定します。再帰レベル-1は発生していないため、バックリファレンスはマッチに失敗します。これはエラーではなく、単に非参加キャプチャグループへのバックリファレンスです。しかし、バックリファレンスには選択肢があります。zz\bは文字列の終わりでマッチします。abcdefdcbazが正常にマッチしました。

これは好きなだけ続けることができます。正規表現\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-2'|z)|[a-z])\babcdefcbazz. \b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter-99'|z)|[a-z])\babcdefzzzzzz.

反対方向に行く場合、\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+1'|z)|[a-z])\babcdefzedcb再び、大量のマッチとバックトラッキングの後、2番目の[a-z]f正規表現エンジンは再帰レベル4に戻り、グループ「letter」はa, b, c, d、そしてeを再帰レベル0~4にスタックに持っている。

ここで、エンジンはバックリファレンス\k'letter+1'現在のレベルは4で、バックリファレンスは+1を指定しています。キャプチャグループは再帰レベル5でバックトラックされました。つまり、非参加グループへのバックリファレンスがあり、マッチに失敗します。選択肢のzはマッチします。エンジンは4回目の再帰から抜ける。

再帰レベル3において、後方参照は再帰レベル4を指します。キャプチャグループは再帰レベル4で正常にマッチしたため、正規表現エンジンがその再帰から既に脱出したとしても、そのマッチをスタックに保持しています。そのため\k'letter+1'e・再帰レベル3は正常に終了します。

バックリファレンスは、正規表現エンジンが最初の再帰から抜けるまでdcにマッチし続けます。ここで、すべての再帰の外で、正規表現エンジンは再び\k'letter+1'・現在のレベルは0であり、後方参照は+1を指定します。キャプチャグループは、それ以前のすべての成功した再帰レベルを保持しています。そのため、後方参照は依然としてb最初の再帰中にグループがキャプチャした\bは文字列の終わりでマッチします。abcdefzdcbが正常にマッチしました。

にマッチすることができます。この方向にも、好きなだけ続けることができます。正規表現\b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+2'|z)|[a-z])\babcdefzzedc. \b(?'word'(?'letter'[a-z])\g'word'(?:\k'letter+99'|z)|[a-z])\babcdefzzzzzz.