ネストされたキャプチャグループは、正規表現でどのように番号付けされますか?


84

正規表現がネストされた括弧のキャプチャ動作をどのように処理するかについて定義された動作はありますか?より具体的には、異なるエンジンが最初の位置で外側の括弧をキャプチャし、後続の位置でネストされた括弧をキャプチャすることを合理的に期待できますか?

次のPHPコードを検討してください(PCRE正規表現を使用)

<?php
  $test_string = 'I want to test sub patterns';
  preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
  print_r($matches);
?>

Array
(
    [0] => I want to test sub patterns  //entire pattern
    [1] => I want to test           //entire outer parenthesis
    [2] => want             //first inner
    [3] => to               //second inner
    [4] => patterns             //next parentheses set
)

括弧で囲まれた式全体が最初にキャプチャされ(テストしたい)、次に内側の括弧で囲まれたパターンがキャプチャされます(「want」および「to」)。これは論理的には理にかなっていますが、最初にサブ括弧をキャプチャし、次にパターン全体をキャプチャするために、同様に論理的なケースが作成されていることがわかりました。

つまり、これは正規表現エンジンで定義された「最初にすべてをキャプチャする」動作ですか、それともパターンのコンテキストやエンジンの動作に依存しますか(PCREはC#とは異なり、Javaとは異なります)などより)?


すべての正規表現フレーバーに本当に興味がある場合は、「言語に依存しない」タグが必要です。すべてをリストするにはフレーバーが多すぎるため、それらのほとんどは実際の標準に準拠していません(ただし、キャプチャグループの番号付けに関しては非常に一貫性があります)。
アランムーア

このグループには、$ 1、$ 2、$ 3 ....などを使用してアクセスできます。10番目のグループにアクセスするにはどうすればよいですか?10ドルになりますか?$ 1の後に0が続くと解釈されるため、$ 10が機能するとは思いません。これは、最大9つのグループしか持てないという意味ですか?作成者が質問の一部としてこれを含めることができれば、これは正規表現のネストされたグループについてすべてを知るための単一の場所になります。
LionHeart 2010

回答:


59

perlrequickから

正規表現のグループがネストされている場合、$ 1は左端の開き括弧、$ 2は次の開き括弧などのグループを取得します。

警告:非キャプチャグループの開き括弧を除く(?=)

更新

私は一般的に本物を使用しているので、PCREはあまり使用しません;)が、PCREのドキュメントはPerlのドキュメントと同じように表示されます。

SUBPATTERNS

2.サブパターンをキャプチャサブパターンとして設定します。これは、パターン全体が一致すると、サブパターンに一致したサブジェクト文字列のその部分が、のovector引数を介して呼び出し元に返されることを意味しますpcre_exec()。開き括弧は、キャプチャするサブパターンの数を取得するために、左から右に(1から開始して)カウントされます。

たとえば、文字列「赤い王」がパターンと一致する場合

the ((red|white) (king|queen))

キャプチャされた部分文字列は「redking」、「red」、および「king」であり、それぞれ1、2、および3の番号が付けられています。

PCREがPerl正規表現の互換性から逸脱している場合は、頭字語を再定義する必要があります。「Perl Cognate Regular Expressions」、「Perl ComparableRegularExpressions」などです。または単に意味の文字を売却します。


1
@Sinan:彼はPHPでPCREを使用しています。これは「Perl互換の正規表現」です。したがって、Perlを直接使用するのとまったく同じである必要があります
Pascal MARTIN

3
パスカル、PCREはPerl互換の正規表現セットになる試みとして始まりましたが、近年、2つはわずかに分かれています。それでも非常に似ていますが、高度な機能セットには微妙な違いがあります。(また、質問によると、私はすべてのプラットフォームに興味があります)
Alan Storm

1
実際、最近の「漂流」のほとんどを行っているのはPerlですが、その通りです。「Perl互換」は、誤った名称から前後関係のないものに急速に変化しています。:D
アランムーア

1
@アラン、Perlは間違いなく動いています。P5.10はいくつかの点を変更しましたが、6は大きく異なります。Pはほぼ確実に「Perl5」として解釈される必要があります。PCREは素晴らしいプロジェクトであり、私は十分に称賛することはできません。それは、いくつかのプロジェクトの天の恵みでした。
daotoad 2009

1
これを最初の引用の下に追加しました。警告:非キャプチャグループの開始括弧(?=)を除きます。編集したときにログインしていないことに気づきました。このコメントを追加したときだけ、資格情報の入力を求められました。したがって、承認するにはもう1人必要です。
JGFMK 2018年

17

ええ、これはあなたが興味を持っているすべての言語に対してかなりよく定義されています:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    "キャプチャグループは、左から右に開いた括弧を数えることによって番号が付けられます。...グループゼロは常に式全体を表します。」
  • .Net - http:
    //msdn.microsoft.com/en-us/library/bs2twtah(VS.71).aspx "()を使用したキャプチャには、最初の括弧の順序に基づいて自動的に番号が付けられます。キャプチャ、キャプチャ要素番号0は、正規表現パターン全体と一致するテキストです。 ")
  • PHP(PCRE関数) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    「\ 0または$ 0はパターン全体にマッチするテキストを参照します。開始括弧は、キャプチャサブパターンの数を取得するために左から右に(1から開始して)カウントされます。」(非推奨のPOSIX関数にも当てはまりました)
  • PCRE - http://www.pcre.org/pcre.txt
    Alan Mの発言に追加するには、「pcre_exec()がキャプチャされた部分文字列を返す方法」を検索し、次の5番目の段落を読みます。

    整数の最初のペアであるovector [0]とovector [1]は、
    パターン全体と一致するサブジェクト文字列の一部。次
    ペアは、最初のキャプチャサブパターンなどに使用されます。値
    pcre_exec()によって返されるのは、最も大きい番号のペアより1つ多い
    設定されています。たとえば、2つの部分文字列がキャプチャされている場合、
    戻り値は3です。キャプチャサブパターンがない場合、戻り値は3です。
    成功した一致の値は1で、最初のペアだけであることを示します
    オフセットのが設定されています。
    
  • Perlの異なる- http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1、$ 2など、あなたが期待するようグループをキャプチャ一致する(すなわち開口部ブラケットの発生による)、しかし$ 0戻りプログラム名ではなく、クエリ文字列全体-代わりに$&を使用します。

他の言語(Python、Rubyなど)でも同様の結果が得られる可能性が高くなります。

内側のキャプチャグループを最初にリストすることも同様に論理的であり、その通りです。これは、パレンを開くのではなく、閉じるときにインデックスを作成するだけの問題です。(私があなたを正しく理解している場合)。ただし、これを行うのは自然ではありません(たとえば、読み取り方向の規則に従わないため)。そのため、特定の結果インデックスにどのキャプチャグループが含まれるかを検査によって決定することがより困難になります(おそらく大幅ではありません)。

一致文字列全体を位置0に配置することも理にかなっています-主に一貫性のためです。これにより、正規表現から正規表現へのキャプチャグループの数に関係なく、また実際に何かに一致するキャプチャグループの数に関係なく、一致した文字列全体を同じインデックスに保つことができます(たとえば、Javaは、キャプチャごとに一致したグループの配列の長さを折りたたみます。グループはどのコンテンツとも一致しません(たとえば、「(。*)pattern」のようなものを考えてください)。capture_group_results[capturing_group_results_length-2]はいつでも検査できますが、動的に変数を作成するPerlへの言語($ 1)にはうまく変換されません。 、$ 2など)(Perlは、一致する式に$&を使用するため、もちろん悪い例ですが、アイデアは得られます:)。


1
いい答えです。しかし、Python(2&3)の更新もどう
ですか

JavaScriptはどうですか!?!
mesqueeb

9

私が知っているすべての正規表現フレーバーは、開き括弧が表示される順序でグループに番号を付けます。含まれるサブグループの前に外部グループに番号が付けられるのは自然な結果であり、明示的なポリシーではありません。

興味深いのは、名前付きグループです。ほとんどの場合、それらは、親の相対的な位置による番号付けの同じポリシーに従います。名前は、番号の単なるエイリアスです。ただし、.NET正規表現では、名前付きグループには番号付きグループとは別に番号が付けられます。例えば:

Regex.Replace(@"one two three four", 
              @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
              @"$1 $2 $3 $4")

// result: "two four one three"

事実上、番号名前のエイリアスです。名前付きグループに割り当てられた番号は、「実際の」番号付きグループが省略されたところから始まります。これは奇妙なポリシーのように思えるかもしれませんが、それには十分な理由があります。.NET正規表現では、同じグループ名を正規表現で複数回使用できます。これにより、このスレッドのような正規表現を使用して、さまざまなロケールの浮動小数点数を照合できます

^[+-]?[0-9]{1,3}
(?:
    (?:(?<thousand>\,)[0-9]{3})*
    (?:(?<decimal>\.)[0-9]{2})?
|
    (?:(?<thousand>\.)[0-9]{3})*
    (?:(?<decimal>\,)[0-9]{2})?
|
    [0-9]*
    (?:(?<decimal>[\.\,])[0-9]{2})?
)$

千の区切り記号がある場合、正規表現のどの部分が一致したかに関係なく、グループ「千」に保存されます。同様に、小数点記号(ある場合)は常にグループ「decimal」に保存されます。もちろん、再利用可能な名前付きグループなしでセパレーターを識別して抽出する方法はありますが、この方法は非常に便利であり、奇妙な番号付けスキームを正当化する以上のものだと思います。

そして、Perl 5.10+があります。これにより、私が何をすべきかを知っているよりも、グループのキャプチャをより細かく制御できます。:D


4

左のparenの順序でキャプチャする順序は、私が作業したすべてのプラットフォームで標準です。(perl、php、ruby、egrep)


「左のパレンの順序でキャプチャする」それをありがとう、それは振る舞いを説明するはるかに簡潔な方法です。
アランストーム

1
あなたは、Perl 5.10、とPerl 6でキャプチャ-番号を再することができます
ブラッド・ギルバート
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.