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

名前付きキャプチャグループと後方参照

ほぼすべての最新の正規表現エンジンは、番号付きキャプチャグループ番号付き後方参照をサポートしています。多くのグループと後方参照を持つ長い正規表現は、読みにくい場合があります。特に、正規表現の途中でキャプチャグループを追加または削除すると、追加または削除されたグループに続くすべてのグループの番号がずれてしまうため、保守が困難になる可能性があります。

Pythonのreモジュールは、名前付きキャプチャグループと名前付き後方参照を提供した最初のモジュールでした。(?P<name>group)は、groupの一致を後方参照「name」にキャプチャします。nameは、文字で始まる英数字のシーケンスである必要があります。groupは任意の正規表現です。名前付き後方参照(?P=name)を使用して、グループの内容を参照できます。疑問符、P、山括弧、等号はすべて構文の一部です。名前付き後方参照の構文では括弧を使用していますが、これはキャプチャやグループ化を行わない後方参照にすぎません。HTMLタグの例は、次のように記述できます。<(?P<tag>[A-Z][A-Z0-9]*)\b[^>]*>.*?</(?P=tag)>.

.NETも名前付きキャプチャをサポートしています。Microsoftの開発者は、Pythonによって開拓されPCRE(当時名前付きキャプチャをサポートしていた唯一の2つの正規表現エンジン)によってコピーされた構文に従うのではなく、独自の構文を発明しました。(?<name>group)または(?'name'group)は、groupの一致を後方参照「name」にキャプチャします。名前付き後方参照は\k<name>または\k'name'です。Pythonと比較すると、名前付きグループの構文にPがありません。名前付き後方参照の構文は、Pythonで使用されているものよりも、番号付き後方参照の構文に似ています。名前の周りに一重引用符または山括弧を使用できます。これは、正規表現にはまったく影響しません。どちらのスタイルも交換して使用できます。山括弧を使用する構文は、文字列を区切るために一重引用符を使用するプログラミング言語で推奨されます。一方、一重引用符を使用する構文は、正規表現をリテラル文字列またはXMLコンテンツとしてフォーマットするために行う必要があるエスケープの量を最小限に抑えるため、正規表現をXMLファイルに追加する場合に推奨されます。

Pythonと.NETは独自の構文を導入したため、これらの2つのバリアントを、名前付きキャプチャと名前付き後方参照の「Python構文」と「.NET構文」と呼びます。現在、他の多くの正規表現フレーバーがこの構文をコピーしています。

Perl 5.10では、名前付きキャプチャと後方参照のPython構文と.NET構文の両方がサポートされるようになりました。また、名前付き後方参照の構文バリアントが2つ追加されました。\k{one}\g{two}です。Perlの名前付き後方参照の5つの構文間に違いはありません。すべて交換して使用できます。置換テキストでは、変数$+{name}を補間して、名前付きキャプチャグループによって一致したテキストを挿入できます。

PCRE 7.2以降では、Perl 5.10がサポートする名前付きキャプチャと後方参照のすべての構文がサポートされています。PCREの古いバージョンでは、当時「Perl互換」ではなかったにもかかわらず、Python構文がサポートされていました。PHPDelphiRなどのPCREを使用して正規表現サポートを実装する言語も、このすべての構文をサポートしています。残念ながら、PHPとRはどちらも置換テキストでの名前付き参照をサポートしていません。名前付きグループへの番号付き参照を使用する必要があります。PCREは、検索と置換をまったくサポートしていません。

Java 7XRegExpは.NET構文をコピーしましたが、山括弧付きのバリアントのみをコピーしました。Ruby 1.9は、.NET構文の両方のバリアントをサポートしています。JGsoftフレーバーは、Python構文と.NET構文の両方のバリアントをサポートしています。

Boost 1.42以降では、山括弧または引用符付きの.NET構文を使用する名前付きキャプチャグループと、Perl 5.10の中括弧付きの\g構文を使用する名前付き後方参照がサポートされています。Boost 1.47では、さらに\k構文を使用する後方参照が、.NETの山括弧と引用符付きでサポートされています。Boost 1.47では、これらのバリアントを乗算することができました。Boost 1.47では、名前付きおよび番号付き後方参照を\gまたは\kと、中括弧、山括弧、または引用符付きで指定できます。そのため、Boost 1.47以降では、基本的な\1構文に加えて、後方参照構文のバリエーションが6つあります。これにより、BoostはRuby、PCRE、PHP、R、およびJGsoftと競合します。これらは、山括弧または引用符付きの\gサブルーチンコールとして扱います。

名前付きキャプチャグループの番号

名前付きキャプチャグループと番号付きキャプチャグループを混在させることはお勧めしません。これは、グループの番号付け方法にフレーバー間で一貫性がないためです。グループに名前を付ける必要がない場合は、(?:group)構文を使用して、キャプチャしないようにします。.NETでは、RegexOptions.ExplicitCaptureを設定することで、すべての名前のないグループをキャプチャしないようにできます。Delphiでは、roExplicitCaptureを設定します。XRegExpでは、/nフラグを使用します。Perlは、Perl 5.22以降で/nをサポートしています。PCREでは、PCRE_NO_AUTO_CAPTUREを設定します。JGsoftフレーバーと.NETは、(?n) モード修飾子をサポートしています。すべての名前のないグループをキャプチャしないようにすると、このセクションをスキップして、頭痛の種を減らすことができます。

ほとんどのフレーバーは、名前付きキャプチャグループと名前のないキャプチャグループの両方に、左から右に開始括弧を数えることによって番号を付けます。既存の正規表現に名前付きキャプチャグループを追加すると、名前のないグループの番号がずれてしまいます。ただし、.NETでは、名前のないキャプチャグループに最初に番号が割り当てられ、すべての名前付きグループをスキップして、左から右に開始括弧がカウントされます。その後、名前付きグループには、名前付きグループの開始括弧を左から右に数えることによって、続く番号が割り当てられます。

JGsoft正規表現エンジンは、Pythonと.NETがPython構文を使用し、.NETのみが.NET構文を使用していたときに、Pythonと.NETの構文をコピーしました。そのため、Pythonと.NETの番号付け動作もコピーされたため、Pythonと.NET向けの正規表現はその動作を維持します。Pythonの場合と同様に、名前のないグループと一緒にPythonスタイルの名前付きグループに番号が付けられます。.NETの場合と同様に、.NETスタイルの名前付きグループには後で番号が付けられます。これらのルールは、同じ正規表現で両方のスタイルを混在させた場合でも適用されます。

例として、正規表現(a)(?P<x>b)(c)(?P<y>d)は、abcdに一致すると予想されます。この正規表現と置換\1\2\3\4または$1$2$3$4(フレーバーによって異なります)を使用して検索と置換を実行すると、abcdが得られます。4つのグループすべてに、1から4までの番号が左から右に付けられました。

.NETでは、状況はもう少し複雑です。正規表現(a)(?<x>b)(c)(?<y>d)は再びabcdに一致します。ただし、置換として$1$2$3$4を使用して検索と置換を実行すると、acbdが得られます。最初に、名前のないグループ(a)(c)に番号1と2が付けられました。次に、名前付きグループ「x」と「y」に番号3と4が付けられました。

.NET構文をコピーした他のすべてのフレーバーでは、正規表現(a)(?<x>b)(c)(?<y>d)はまだabcdに一致します。ただし、JGsoftフレーバーを除くこれらすべてのフレーバーでは、置換\1\2\3\4または$1$2$3$4(フレーバーによって異なります)を実行すると、abcdが得られます。4つのグループすべてに、左から右に番号が付けられました。

JGsoftフレーバーを使用するPowerGREPでは、名前付きキャプチャグループが特別な役割を果たします。同じ名前のグループは、同じPowerGREPアクション内のすべての正規表現と置換テキスト間で共有されます。これにより、アクションのある部分で名前付きキャプチャグループによってキャプチャされたものを、アクションの後の部分で参照できます。このため、PowerGREPでは、名前付きキャプチャグループへの番号による参照は一切許可されていません。正規表現で名前付きグループと番号付きグループを混在させる場合、番号付きグループは、JGsoftフレーバーが常にそうであるように、Pythonと.NETのルールに従って番号が付けられます。

同名の複数のグループ

.NETフレームワークJGsoftフレーバーでは、正規表現内の複数のグループに同じ名前を付けることができます。同じ名前のすべてのグループは、一致するテキストに対して同じストレージを共有します。したがって、その名前への後方参照は、最後に何かをキャプチャした、その名前のグループによって一致したテキストと一致します。置換テキスト内の名前への参照は、最後に何かをキャプチャした、その名前のグループによって一致したテキストを挿入します。

PerlRubyも同名のグループを許可します。しかし、これらのフレーバーは、同じ名前のすべてのグループが1つのように動作するように見せるためのトリックを使用しているだけです。実際には、グループは別々です。Perlでは、後方参照は、正規表現内でその名前を持ち、何かと一致した最も左側のグループによってキャプチャされたテキストと一致します。Rubyでは、後方参照は、その名前のいずれかのグループによってキャプチャされたテキストと一致します。バックトラッキングにより、Rubyはすべてのグループを試します。

そのため、PerlとRubyでは、同じ名前のグループが正規表現内の別々の選択肢にある場合にのみ、意味のある方法で使用できます。そのため、その名前のグループの1つだけがテキストをキャプチャできます。そして、そのグループへの後方参照は、グループによってキャプチャされたテキストと理にかなって一致します。

たとえば、「a」の後に数字0〜5、または「b」の後に数字4〜7が続き、数字のみが必要な場合は、正規表現a(?<digit>[0-5])|b(?<digit>[4-7])を使用できます。これらの4つのフレーバーでは、「digit」という名前のグループは、文字に関係なく、一致した数字0〜7を提供します。この一致の後にcとまったく同じ数字が続くようにするには、(?:a(?<digit>[0-5])|b(?<digit>[4-7]))c\k<digit>

を使用できます。PCREは、デフォルトでは重複した名前付きグループを許可しません。PCRE 6.7以降では、そのオプションを有効にするか、モード修飾子(?J)を使用すると、重複した名前付きグループが許可されます。ただし、PCRE 8.36より前では、後方参照は常に正規表現内でその名前を持つ最初のキャプチャグループを指していたため、一致に参加したかどうかに関係なく、あまり役に立ちませんでした。PCRE 8.36(およびPHP 5.6.9とR 3.1.3)以降、およびPCRE2では、後方参照は実際に一致に参加したその名前の最初のグループを指します。PCREとPerlは重複グループを反対方向に処理しますが、別々の選択肢で同じ名前のグループのみを使用するというアドバイスに従えば、最終結果は同じになります。

Boostは重複した名前付きグループを許可します。Boost 1.47より前では、後方参照は常に正規表現内の後方参照の前に出現するその名前の最後のグループを指していたため、役に立ちませんでした。Boost 1.47以降では、後方参照はPCRE 8.36以降と同様に、実際に一致に参加したその名前の最初のグループを指します。

Python、Java、およびXRegExp 3では、複数のグループで同じ名前を使用することはできません。そうすると、正規表現のコンパイルエラーが発生します。XRegExp 2では許可されていましたが、正しく処理されませんでした。

Perl 5.10、PCRE 8.00、PHP 5.2.14、およびBoost 1.42(またはこれらの以降のバージョン)では、異なる選択肢のグループに同じ名前を付けたい場合は、分岐リセットグループを使用するのが最善です。(?|a(?<digit>[0-5])|b(?<digit>[4-7]))c\k<digit>のように。この特別な構文(グループは(?|ではなく(?:で開かれます)を使用すると、「digit」という名前の2つのグループは実際には同じグループになります。そして、そのグループへの後方参照は、これらのフレーバー間で常に正しく一貫して処理されます。(PCREとPHPの古いバージョンは分岐リセットグループをサポートしている場合がありますが、分岐リセットグループ内の重複名を正しく処理しません。)