クイックスタート
チュートリアル
ツールと言語
リファレンス
書籍レビュー
正規表現チュートリアル
はじめに
目次
特殊文字
表示不可能な文字
正規表現エンジンの内部
文字クラス
文字クラスの減算
文字クラスの積集合
短縮文字クラス
ドット
アンカー
単語境界
選択
オプション項目
繰り返し
グループ化とキャプチャ
後方参照
後方参照 パート2
名前付きグループ
相対後方参照
分岐リセットグループ
フリースペーシングとコメント
Unicode
モード修飾子
アトミックグループ
所有量指定子
先読みと後読み
先読みと後読み パート2
一致からテキストを除外する
条件式
バランスグループ
再帰
サブルーチン
無限再帰
再帰と量指定子
再帰とキャプチャ
再帰と後方参照
再帰とバックトラッキング
POSIX ブラケット式
長さゼロの一致
継続一致
このサイトの詳細
はじめに
正規表現クイックスタート
正規表現チュートリアル
置換文字列チュートリアル
アプリケーションと言語
正規表現の例
正規表現リファレンス
置換文字列リファレンス
書籍レビュー
印刷可能なPDF
このサイトについて
RSSフィードとブログ
RegexBuddy—Better than a regular expression tutorial!

サブルーチン呼び出しはキャプチャする場合としない場合があります

このチュートリアルでは、正規表現サブルーチンを、正確に一致させたい次の例を用いて紹介しました。

Name: John Doe
Born: 17-Jan-1964
Admitted: 30-Jul-2013
Released: 3-Aug-2013

RubyまたはPCREでは、次の正規表現を使用できます。

^Name:(.*)\n
Born:(?'date'(?:3[01]|[12][0-9]|[1-9]) - (?:Jan|Feb|...|Dec) - (?:19|20)[0-9][0-9])\n
Admitted:\g'date'\n
Released:\g'date'$

Perlでは、PCREでも動作するわずかに異なる構文が必要です。

(中略:正規表現部分は i=59 とほぼ同じため省略)(?&date)\n
Released:\ (?&date)$

残念ながら、これらの3つの正規表現フレーバーは、構文以外にもサブルーチン呼び出しの扱い方に違いがあります. まず、Rubyではサブルーチン呼び出しを行うと、キャプチャグループにサブルーチン呼び出し中に一致したテキストが格納されます. Perl、PCRE、およびBoostでは、サブルーチン呼び出しは呼び出されたグループに影響を与えません.

Rubyの解決策が上記のサンプルに一致する場合、キャプチャグループ "date" の内容を取得すると、3-Aug-2013となり、これはそのグループへの最後のサブルーチン呼び出しによって一致したものです. Perlの解決策が同じに一致する場合、$+{date}を取得すると、17-Jan-1964となります。Perlでは、サブルーチン呼び出しは何もキャプチャしませんでした. しかし、「Born」の日付は、一致したテキストを通常どおり格納する通常の名前付きキャプチャグループと一致しました. グループへのサブルーチン呼び出しは、それを変更しません. PCREは、PCREでRuby構文を使用する場合でも、この場合はPerlと同じように動作します.

JGsoft V2は、最初の正規表現を使用するとRubyのように動作します.\g構文はRubyの発明であり、後にPCREによってコピーされたという事実によって、これを覚えることができます. JGsoft V2は、2番目の正規表現を使用するとPerlのように動作します. Perlは手続き型コードでもサブルーチン呼び出しにアンパサンドを使用しているという事実によって、これを覚えることができます.

一致から日付を抽出したい場合、最良の解決策は、各日付に別のキャプチャグループを追加することです. そうすれば、「date」グループによって格納されたテキストと、これらのフレーバー間のこの特定の違いを無視できます. RubyまたはPCREでは

(中略:正規表現部分は複雑なため省略。各日付を個別にキャプチャするよう変更されている)

Perlでは、PCREでも動作するわずかに異なる構文が必要です。

(中略:正規表現部分は i=74 とほぼ同じため省略)

再帰またはサブルーチン呼び出し内のキャプチャグループ

正規表現が他のキャプチャグループを含むキャプチャグループへのサブルーチン呼び出しまたは再帰呼び出しを行う場合、Perl、PCRE、およびRubyの間にはさらに違いがあります. 同じ問題は、キャプチャグループが含まれている場合、正規表現全体の再帰にも影響します. このトピックの残りの部分では、「再帰」という用語は、正規表現全体の再帰、キャプチャグループへの再帰、またはキャプチャグループへのサブルーチン呼び出しに等しく適用されます.

PCREとBoostは、再帰の開始時と終了時にキャプチャグループをバックアップおよび復元します。正規表現エンジンが再帰に入ると、内部的にすべてのキャプチャグループのコピーを作成します。これはキャプチャグループには影響しません。再帰内部の逆参照は、参照先のグループが再帰中に何かをキャプチャするまで、再帰の前にキャプチャされたテキストと一致します。再帰後、すべてのキャプチャグループは、再帰の開始時に作成された内部コピーに置き換えられます。再帰中にキャプチャされたテキストは破棄されます。これは、キャプチャグループを使用して、再帰中に一致したテキストの部分を取得できないことを意味します。

再帰を備えた最初のバージョンであるPerl 5.10からバージョン5.18までは、各再帰レベル間でキャプチャグループが分離されていました。Perl 5.10の正規表現エンジンが再帰に入ると、すべてのキャプチャグループは、まだ一致に参加していないかのように表示されます。最初は、すべての逆参照は失敗します。再帰中は、キャプチャグループは通常どおりキャプチャします。逆参照は、同じ再帰中にキャプチャされたテキストと通常どおり一致します。正規表現エンジンが再帰から抜けると、すべてのキャプチャグループは再帰前の状態に戻ります。Perl 5.20では、Perlの動作が変更され、PCREと同じ方法でキャプチャグループをバックアップおよび復元するようになりました。

しかし、ほとんどの実用的目的では、対応するキャプチャグループの後にのみ逆参照を使用します。そのため、Perl 5.10から5.18が再帰中にキャプチャグループを処理する方法と、PCREとそれ以降のバージョンのPerlが処理する方法の違いは、学術的なものです。

Rubyの動作はまったく異なります。Rubyの正規表現エンジンが再帰に入るか抜けるとき、キャプチャグループによって格納されたテキストはまったく変更されません。逆参照は、発生した可能性のある再帰に関係なく、グループの最後の一致中にキャプチャグループによって格納されたテキストと一致します。全体の一致が見つかった後、各キャプチャグループは、最後の一致が再帰中であったとしても、最後の一致のテキストを格納します。これは、キャプチャグループを使用して、最後の再帰中に一致したテキストの一部を取得できることを意味します。

JGsoft V2は、Rubyから借用した\g構文を使用する場合、Rubyのように動作します。他の構文を使用する場合は、Perl 5.20およびPCREのように動作します。

PerlとPCREにおける奇数長の回文

PerlとPCREでは、\b(?'word'(?'letter'[a-z])(?&word)\k'letter'|[a-z])\bを使用して、a, dad, radar, racecar、およびredividerなどの回文と一致させることができます。この正規表現は、文字数が奇数の回文のみに一致します。これは、英語のほとんどの回文を網羅しています。偶数文字の回文も処理するように正規表現を拡張するには、PerlとPCREが再帰の失敗後にバックトラックする方法の違いを考慮する必要があります。これは、このチュートリアルの後半で説明します。これらの違いは、対象文字列が回文ではなく、一致が見つからない場合にのみ発生するため、ここではこれらを無視します。

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

(?&word)が一致に失敗したため、(?'letter'[a-z])は一致を諦める必要があります。グループは、再帰の開始時にグループが保持していたテキストであるaに戻ります。(Perl 5.18以前では空になります。)繰り返しますが、これは問題ではありません。正規表現エンジンは、グループ「word」内の2番目の選択肢を試す必要があるためです。2番目の[a-z]は文字列の最後のrと一致します。エンジンは正常な再帰から抜け出します。グループ「letter」によって格納されたテキストは、4番目の再帰に入る前にキャプチャされたa.

に復元されます。(?&word)と一致した後、エンジンは\k'letter'に到達します。逆参照は、正規表現エンジンがすでに対象文字列の末尾に到達しているため失敗します。そのため、もう一度バックトラックし、キャプチャグループにaを諦めさせます。2番目の選択肢はaと一致します。正規表現エンジンは3番目の再帰から抜け出します。グループ「letter」は、2番目の再帰中に一致したdに復元されます。

正規表現エンジンは再び(?&word)と一致しました。逆参照は、グループがdを格納しているのに、文字列の次の文字がrであるため失敗します。再びバックトラックすると、2番目の選択肢はdと一致し、グループは最初の再帰中に一致したaに復元されます。

ここで、\k'letter'は文字列の2番目のaと一致します。これは、正規表現エンジンが、キャプチャグループが最初のaと一致した最初の再帰に戻ったためです。正規表現エンジンは最初の再帰を終了します。キャプチャグループは、最初の再帰の前に一致したrに復元されます。

最後に、逆参照は2番目のrと一致します。エンジンはもはや再帰内にはないため、グループの後の正規表現の残りの部分に進みます。\bは文字列の末尾と一致します。正規表現の末尾に到達し、radarが全体の一致として返されます。一致後にグループ「word」と「letter」を照会すると、radarrが取得されます。これは、すべての再帰の外側でこれらのグループによって一致したテキストです。

この正規表現がRubyで機能しない理由

Rubyでこの方法で回文と一致させるには、再帰レベルを指定する特別な逆参照を使用する必要があります。次のように通常の逆参照を使用すると、\b(?'word'(?'letter'[a-z])\g'word'\k'letter'|[a-z])\bRubyは文句を言いませんが、4文字以上の回文とは一致しません。代わりに、この正規表現はa, dad, radaa, raceccc、およびrediviiii.

のようなものと一致します。この正規表現がradarと一致しない理由を見てみましょう。RubyはPerlとPCREのように開始し、文字列に[a-z]と一致する文字が残っていない再帰に入ります。

\g'word'が一致に失敗したため、(?'letter'[a-z])は一致を諦める必要があります。Rubyは、グループが最後一致したテキストであるaに戻します。文字列の2番目の[a-z]は文字列の最後のrです。エンジンは正常な再帰から抜け出します。グループ「letter」は、最後一致したa.

に復元されます。\g'word'と一致した後、エンジンは\k'letter'を保持し続けます。逆参照は、正規表現エンジンがすでに対象文字列の末尾に到達しているため失敗します。そのため、もう一度バックトラックし、グループを前一致したdを諦めさせます。2番目の選択肢はaに戻します。正規表現エンジンは3番目の再帰から抜け出します。

正規表現エンジンは再び\g'word'と一致しました。逆参照は、グループがdを格納しているのに、文字列の次の文字がr。再びバックトラックすると、グループはaに戻り、2番目の選択肢は文字列のd.

ここで、\k'letter'は文字列の2番目のaと一致します。正規表現エンジンは、adaと正常一致した最初の再帰を終了します。キャプチャグループは、バックトラックされなかった最後一致であるaを保持し続けます。

正規表現エンジンは文字列の最後の文字にあります。この文字はrです。逆参照は、グループがまだaを保持しているため失敗します。エンジンはもう一度バックトラックし、(?'letter'[a-z])\g'word'\k'letter'に、これまでに一致したradaを諦めさせます。正規表現エンジンは文字列の先頭に戻っています。それでも、グループの2番目の選択肢を試すことができます。これは、文字列の最初のrと一致します。エンジンはもはや再帰内にはないため、グループの後の正規表現の残りの部分に進みます。\bは、最初のrの後一致に失敗します。正規表現エンジンは試行する順列がなくなりました。一致の試行は失敗しました。

対象文字列がradaaの場合、Rubyのエンジンは上記とほぼ同じ一致プロセスを実行します。最後の段落で説明されているイベントのみが変更されます。正規表現エンジンが文字列の最後の文字に到達すると、その文字はaになります。今回は、逆参照は一致します。エンジンはもはや再帰内にはないため、グループの後の正規表現の残りの部分に進みます。\bは文字列の末尾と一致します。正規表現の末尾に到達し、radaaが全体の一致として返されます。一致後にグループ「word」と「letter」を照会すると、radaaa。これらは、バックトラックされなかったこれらのグループの最後一致です。

基本的に、Rubyでは、この正規表現は、文字数が奇数で、中央の文字の右側のすべての文字が中央の文字のすぐ左側の文字と同じである単語と一致します。これは、Rubyがキャプチャグループをバックトラック時にのみ復元し、再帰から抜けるときには復元しないためです。

Rubyに固有の解決策は、このページの正規表現で使用されている通常の逆参照ではなく、再帰レベルを指定する逆参照を使用することです。