web-dev-qa-db-ja.com

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

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

以下を考慮してください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とは異なります)など)?

72
Alan Storm

perlrequick から

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

Caveat:非キャプチャグループの開始かっこ(?=)を除く

更新

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

サブパターン

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

たとえば、文字列「the red king」がパターンと一致する場合

_the ((red|white) (king|queen))
_

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

PCREがPerl正規表現の互換性から離れている場合、おそらく頭字語を再定義する必要があります。「Perl Cognate Regular Expressions」、「Perl Comparable Regular Expressions」などです。または、単に意味の文字を取り上げます。

54
daotoad

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

  • Java- http://Java.Sun.com/javase/6/docs/api/Java/util/regex/Pattern。 html#cg
    「キャプチャグループは、左括弧を左から右に数えることによって番号が付けられます。..グループ0は常に式全体を表します。」
  • 。Net- http://msdn.Microsoft.com/en-us/library/bs2twtah(VS.71).aspx
    「()を使用するキャプチャには、1から始まる開き括弧の順序に基づいて自動的に番号が付けられます。最初のキャプチャであるキャプチャ要素番号ゼロは、正規表現パターン全体と一致するテキストです。)
  • 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
    アランMが言ったことに追加するには、「pcre_exec()がキャプチャされた部分文字列を返す方法」を検索し、次の5番目の段落を読みます。

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

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

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

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

16
Alan Donnelly

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

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

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

// result: "two four one three"

実際には、numbername;のエイリアスです。名前付きグループに割り当てられた番号は、「実際の」番号付きグループが終了するところから始まります。それは奇妙なポリシーのように思えるかもしれませんが、それには十分な理由があります。NET正規表現では、正規表現で同じグループ名を複数回使用できます。これにより、異なるロケールの浮動小数点数に一致する this thread のような正規表現が可能になります。

^[+-]?[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})?
)$

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

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

8
Alan Moore

左括弧の順序でキャプチャする順序は、私が取り組んだすべてのプラットフォームで標準です(Perl、php、Ruby、egrep)

4
Devin Ceartas