クイック スタート
チュートリアル
ツール & 言語
リファレンス
書籍 レビュー
正規表現の例
数値範囲
浮動小数点数
メールアドレス
IPアドレス
有効な日付
数値日付をテキストに変換
クレジットカード番号
完全な行のマッチング
重複行の削除
プログラミング
近くにある2つの単語
落とし穴
壊滅的なバックトラッキング
過剰な繰り返し
サービス拒否
すべてをオプションにする
繰り返しキャプチャグループ
Unicodeと8ビットの混合
このサイトのその他の情報
はじめに
正規表現クイックスタート
正規表現チュートリアル
置換文字列チュートリアル
アプリケーションと言語
正規表現の例
正規表現リファレンス
置換文字列リファレンス
書籍レビュー
印刷可能なPDF
このサイトについて
RSSフィードとブログ
RegexBuddy—The best regular expression debugger!

キャプチャグループの繰り返し vs. 繰り返されるグループのキャプチャ

マッチしたテキストの一部を掴むためにキャプチャグループが必要な正規表現を作成する際、よくある間違いは、繰り返されるグループをキャプチャするのではなく、キャプチャグループを繰り返すことです。違いは、繰り返されるキャプチャグループは最後の反復のみをキャプチャするのに対し、繰り返される別のグループをキャプチャするグループはすべての反復をキャプチャすることです。例を挙げれば、この違いが明確になります。

例えば、次のようなタグにマッチさせたいとしましょう。!abc!または!123!。この2つだけが可能性があり、どのタグが得られたのかを知るためにabcまたは123をキャプチャしたいとします。それは簡単です。!(abc|123)!でうまくいきます。

では、タグにabc123の複数のシーケンスを含めることができるとしましょう。例えば、!abc123!または!123abcabc!。手っ取り早い解決策は!(abc|123)+!です。この正規表現は確かにこれらのタグにマッチします。しかし、タグのラベルをキャプチャグループにキャプチャするという要件は満たさなくなりました。この正規表現が!abc123!にマッチするとき、キャプチャグループは123のみを格納します。次に!123abcabc!にマッチするときは、abc.

のみを格納します。これは、正規表現エンジンが!(abc|123)+!!abc123!にどのように適用するかを見ると簡単に理解できます。まず、!!にマッチします。次に、エンジンはキャプチャグループに入ります。エンジンが対象文字列の最初の文字と2番目の文字の間の位置に到達したときに、キャプチャグループ#1に入ったことを記録します。グループ内の最初のトークンはabcで、これはabcにマッチします。マッチが見つかったため、2番目の代替案は試行されません(エンジンはバックトラッキングの位置を格納しますが、この例では使用されません)。エンジンはキャプチャグループから出ます。エンジンが文字列の4番目と5番目の文字の間の位置に到達したときに、キャプチャグループ#1が終了したことを記録します。

グループから出た後、エンジンはプラスに気づきます。プラスは貪欲なので、グループが再度試行されます。エンジンは再びグループに入り、文字列の4番目と5番目の文字の間でキャプチャグループ#1が入力されたことを記録します。また、プラスが強欲ではないため、バックトラックされる可能性があることも記録します。つまり、グループが2回目にマッチできない場合は問題ありません。このバックトラッキングノートでは、正規表現エンジンはグループの前の反復中のグループの入り口と出口の位置も保存します。

abc123にマッチしませんが、123は成功します。グループが再び終了します。7番目と8番目の文字の間の出口位置が格納されます。

プラスは別の反復を許可するため、エンジンは再度試行します。バックトラッキング情報が格納され、グループの新しい入り口位置が保存されます。しかし、今度はabc123の両方が!にマッチできません。グループが失敗し、エンジンがバックトラックします。バックトラッキング中に、エンジンはグループのキャプチャ位置を復元します。つまり、グループは4番目と5番目の文字の間に入力され、7番目と8番目の文字の間に出力されました。

エンジンは!で、これは!を続行します。全体的なマッチが見つかります。全体的なマッチは、対象文字列全体に及びます。キャプチャグループは文字5、6、7、つまり123を囲みます。マッチが見つかるとバックトラッキング情報は破棄されるため、グループが以前にabcにマッチする反復を持っていたことを後から知る方法はありません。(これに対する唯一の例外は、マッチ試行後もキャプチャグループのバックトラッキング情報を保持する.NET正規表現エンジンです。)

この例でのabc123をキャプチャする解決策は、もう明らかでしょう。正規表現エンジンは、グループに1回だけ出入りする必要があります。これは、プラスがキャプチャグループの外側ではなく内側にある必要があることを意味します。2つの選択肢をグループ化する必要があるため、繰り返されるグループの周りに2番目のキャプチャグループを配置する必要があります。!((abc|123)+)!。この正規表現が!abc123!にマッチすると、キャプチャグループ#1はabc123を格納し、グループ#2は123を格納します。内部グループのマッチには関心がないため、内部グループを非キャプチャにすることでこの正規表現を最適化できます。!((?:abc|123)+)!.