量指定子 (正規表現)

量指定子は、一致と見なされるために入力中に存在する必要がある文字、グループ、または文字クラスの出現数を指定します。 次の表に、.NET でサポートされている量指定子の一覧を示します。

最長一致の量指定子 最短一致の量指定子 説明
* *? 0 回以上一致します。
+ +? 1 回以上一致します。
? ?? 0 回または 1 回一致します。
{n} {n}? 正確に n 回一致します。
{n,} {n,}? n 回以上一致します。
{n,m} {n,m}? n 回以上、m 回以下一致します。

n および m は整数の定数です。 通常、量指定子は最長一致です。 最長一致の場合、正規表現エンジンでは、特定のパターンの出現ができるだけ多くなるように照合が行われます。 ? 文字を量指定子に追加すると、最短一致になります。 最短一致の場合、正規表現エンジンでは、出現回数ができるだけ少なくなるように照合が行われます。 最長一致と最短一致の量指定子の違いの詳細については、このトピックのセクション「最長一致と最短一致の量指定子」を参照してください。

重要

量指定子を入れ子にすると (たとえば、正規表現パターン (a*)*など)、正規表現エンジンで実行する必要がある比較の回数が増加する可能性があります。 比較の回数は、入力文字列の文字数に応じて指数関数的に増加する可能性があります。 この動作と回避方法の詳細については、バックトラッキングに関するページを参照してください。

正規表現の量指定子

以降のセクションでは、.NET の正規表現でサポートされている量指定子について説明します。

注意

正規表現エンジンでは、正規表現パターンで *、+、?、{、および } の各文字を検出すると、文字クラスに含まれているもの以外は量指定子または量指定子コンストラクトの一部として解釈します。 文字クラスの外側でこれらをリテラル文字として解釈するには、文字の前に円記号を付けてエスケープする必要があります。 たとえば、正規表現パターン内の \* という文字列は、リテラルのアスタリスク ("*") 文字と解釈されます。

0 回以上の繰り返しに一致: *

* 量指定子は、直前の要素の 0 回以上の繰り返しに一致します。 これは {0,} 量指定子と同じです。 * は最長一致の量指定子であり、最短一致でこれに対応するのは *? です。

次の例は、この正規表現を示しています。 入力文字列の 9 個の数字グループのうち 5 個がパターンに一致し、4 個 (9592992199919) が一致しません。

string pattern = @"\b91*9*\b";
string input = "99 95 919 929 9119 9219 999 9919 91119";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

// The example displays the following output:
//       '99' found at position 0.
//       '919' found at position 6.
//       '9119' found at position 14.
//       '999' found at position 24.
//       '91119' found at position 33.
Dim pattern As String = "\b91*9*\b"
Dim input As String = "99 95 919 929 9119 9219 999 9919 91119"
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       '99' found at position 0.
'       '919' found at position 6.
'       '9119' found at position 14.
'       '999' found at position 24.
'       '91119' found at position 33.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b 一致がワード境界で開始する必要があることを指定します。
91* 9 の後に 0 個以上の文字 1 が続くパターンと一致します。
9* 0 個以上の文字 9 と一致します。
\b 一致がワード境界で終了する必要があることを指定します。

1 回以上の繰り返しに一致: +

+ 量指定子は、直前の要素の 1 回以上の繰り返しに一致します。 これは {1,} と同じです。 + は最長一致の量指定子であり、最短一致でこれに対応するのは +? です。

たとえば、正規表現 \ban+\w*?\b は、文字 a で始まり、文字 n が 1 回以上繰り返されるすべての単語に一致を試みます。 次の例は、この正規表現を示しています。 この正規表現は、単語 anannualannouncement、および antique に一致し、autumnall では不一致となります。

string pattern = @"\ban+\w*?\b";

string input = "Autumn is a great time for an annual announcement to all antique collectors.";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

// The example displays the following output:
//       'an' found at position 27.
//       'annual' found at position 30.
//       'announcement' found at position 37.
//       'antique' found at position 57.
Dim pattern As String = "\ban+\w*?\b"

Dim input As String = "Autumn is a great time for an annual announcement to all antique collectors."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'an' found at position 27.
'       'annual' found at position 30.
'       'announcement' found at position 37.
'       'antique' found at position 57.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
an+ 後に 1 個以上の文字 n が続く a と一致します。
\w*? 単語文字と 0 回以上 (ただし、できるだけ少ない回数) 一致します。
\b ワード境界で終了します。

0 回または 1 回の繰り返しに一致: ?

? 量指定子は、直前の要素の 0 回または 1 回の繰り返しに一致します。 これは {0,1} と同じです。 ? は最長一致の量指定子であり、最短一致でこれに対応するのは ?? です。

たとえば、正規表現 \ban?\b は、文字 a で始まり、その後に文字 n のインスタンスが 0 回または 1 回続くすべての単語の一致を試みます。 つまり、単語 aan に一致を試みます。 次の例は、この正規表現を示しています。

string pattern = @"\ban?\b";
string input = "An amiable animal with a large snout and an animated nose.";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

// The example displays the following output:
//        'An' found at position 0.
//        'a' found at position 23.
//        'an' found at position 42.
Dim pattern As String = "\ban?\b"
Dim input As String = "An amiable animal with a large snout and an animated nose."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'An' found at position 0.
'       'a' found at position 23.
'       'an' found at position 42.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
an? 後に 0 個または 1 個の文字 n が続く a と一致します。
\b ワード境界で終了します。

n 回の繰り返しに一致: {n}

{n} 量指定子は、直前の要素の n 回の繰り返しに一致します。ここで、n は任意の整数です。 {n} は最長一致の量指定子であり、最短一致でこれに対応するのは {n}? です。

たとえば、正規表現 \b\d+\,\d{3}\b は、ワード境界、1 個以上の 10 進数、3 個の 10 進数、ワード境界の順に続く文字に一致を試みます。 次の例は、この正規表現を示しています。

string pattern = @"\b\d+\,\d{3}\b";
string input = "Sales totaled 103,524 million in January, " +
                      "106,971 million in February, but only " +
                      "943 million in March.";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        '103,524' found at position 14.
//        '106,971' found at position 45.
Dim pattern As String = "\b\d+\,\d{3}\b"
Dim input As String = "Sales totaled 103,524 million in January, " + _
                      "106,971 million in February, but only " + _
                      "943 million in March."
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       '103,524' found at position 14.
'       '106,971' found at position 45.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
\d+ 1 個以上の 10 進数と一致します。
\, コンマ文字と一致します。
\d{3} 3 個の 10 進数と一致します。
\b ワード境界で終了します。

n 回以上の繰り返しに一致: {n,}

{n,} 量指定子は、直前の要素の n 回以上の繰り返しに一致します。ここで、n は任意の整数です。 {n,} は最長一致の量指定子であり、最短一致でこれに対応するのは {n,}? です。

たとえば、正規表現 \b\d{2,}\b\D+ は、ワード境界、2 個以上の 10 進数、ワード境界、数字以外の文字の順に続く文字に一致を試みます。 次の例は、この正規表現を示しています。 この正規表現は、10 進数が 1 個しか含まれていないため、語句 "7 days" とは一致しませんが、語句 "10 weeks" および "300 years" とは正常に一致します。

string pattern = @"\b\d{2,}\b\D+";
string input = "7 days, 10 weeks, 300 years";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        '10 weeks, ' found at position 8.
//        '300 years' found at position 18.
Dim pattern As String = "\b\d{2,}\b\D+"
Dim input As String = "7 days, 10 weeks, 300 years"
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       '10 weeks, ' found at position 8.
'       '300 years' found at position 18.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
\d{2,} 2 個以上の 10 進数と一致します。
\b ワード境界と一致します。
\D+ 1 個以上の 10 進数以外の数字と一致します。

n 回から m 回までの繰り返しに一致: {n,m}

{n,m} 量指定子は、直前の要素の n 回以上、m 回以下の繰り返しに一致します。ここで、nm は整数です。 {n,m} は最長一致の量指定子であり、最短一致でこれに対応するのは {n,m}? です。

次の例では、正規表現 (00\s){2,4} は、2 個の 0 と 1 個のスペースが、2 回以上 4 回以下だけ現れる文字列に一致を試みます。 入力文字列の最後の部分には、このパターンが最大の 4 回ではなく 5 回含まれます。 この部分文字列の最初の部分 (スペースと 5 つ目の 0 のペアまで) だけが正規表現パターンに一致します。

string pattern = @"(00\s){2,4}";
string input = "0x00 FF 00 00 18 17 FF 00 00 00 21 00 00 00 00 00";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        '00 00 ' found at position 8.
//        '00 00 00 ' found at position 23.
//        '00 00 00 00 ' found at position 35.
Dim pattern As String = "(00\s){2,4}"
Dim input As String = "0x00 FF 00 00 18 17 FF 00 00 00 21 00 00 00 00 00"
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       '00 00 ' found at position 8.
'       '00 00 00 ' found at position 23.
'       '00 00 00 00 ' found at position 35.

0 回以上の繰り返しに一致 (最短一致): *?

*? 量指定子は、直前の要素と 0 回以上 (ただし、できるだけ少ない回数) 一致します。 これは、最長一致の量指定子 * に対応する、最短一致の量指定子です。

次の例では、正規表現 \b\w*?oo\w*?\b は、文字列 oo を含むすべての単語に一致します。

 string pattern = @"\b\w*?oo\w*?\b";
 string input = "woof root root rob oof woo woe";
 foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

 //  The example displays the following output:
//        'woof' found at position 0.
//        'root' found at position 5.
//        'root' found at position 10.
//        'oof' found at position 19.
//        'woo' found at position 23.
Dim pattern As String = "\b\w*?oo\w*?\b"
Dim input As String = "woof root root rob oof woo woe"
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'woof' found at position 0.
'       'root' found at position 5.
'       'root' found at position 10.
'       'oof' found at position 19.
'       'woo' found at position 23.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
\w*? 0 個以上 (ただし、できるだけ少ない数) の単語文字と一致します。
oo 文字列 oo と一致します。
\w*? 0 個以上 (ただし、できるだけ少ない数) の単語文字と一致します。
\b ワード境界で終了します。

1 回以上の繰り返しに一致 (最短一致): +?

+? 量指定子は、直前の要素と 1 回以上 (ただし、できるだけ少ない回数) 一致します。 これは、最長一致の量指定子 + に対応する、最短一致の量指定子です。

たとえば、正規表現 \b\w+?\b は、ワード境界で区切られた 1 つ以上の文字に一致します。 次の例は、この正規表現を示しています。

string pattern = @"\b\w+?\b";
string input = "Aa Bb Cc Dd Ee Ff";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        'Aa' found at position 0.
//        'Bb' found at position 3.
//        'Cc' found at position 6.
//        'Dd' found at position 9.
//        'Ee' found at position 12.
//        'Ff' found at position 15.
Dim pattern As String = "\b\w+?\b"
Dim input As String = "Aa Bb Cc Dd Ee Ff"
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'Aa' found at position 0.
'       'Bb' found at position 3.
'       'Cc' found at position 6.
'       'Dd' found at position 9.
'       'Ee' found at position 12.
'       'Ff' found at position 15.

0 回または 1 回の繰り返しに一致 (最短一致): ??

?? 量指定子は、直前の要素と 0 回以上 (ただし、できるだけ少ない回数) 一致します。 これは、最長一致の量指定子 ? に対応する、最短一致の量指定子です。

たとえば、正規表現 ^\s*(System.)??Console.Write(Line)??\(?? は、文字列 Console.Write または Console.WriteLine との一致を試みます。 文字列には、Console の前に System. を含めることができ、後に左かっこを続けることができます。 文字列は行の先頭にある必要がありますが、文字列の前に空白があってもかまいません。 次の例は、この正規表現を示しています。

string pattern = @"^\s*(System.)??Console.Write(Line)??\(??";
string input = "System.Console.WriteLine(\"Hello!\")\n" +
                      "Console.Write(\"Hello!\")\n" +
                      "Console.WriteLine(\"Hello!\")\n" +
                      "Console.ReadLine()\n" +
                      "   Console.WriteLine";
foreach (Match match in Regex.Matches(input, pattern,
                                      RegexOptions.IgnorePatternWhitespace |
                                      RegexOptions.IgnoreCase |
                                      RegexOptions.Multiline))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        'System.Console.Write' found at position 0.
//        'Console.Write' found at position 36.
//        'Console.Write' found at position 61.
//        '   Console.Write' found at position 110.
Dim pattern As String = "^\s*(System.)??Console.Write(Line)??\(??"
Dim input As String = "System.Console.WriteLine(""Hello!"")" + vbCrLf + _
                      "Console.Write(""Hello!"")" + vbCrLf + _
                      "Console.WriteLine(""Hello!"")" + vbCrLf + _
                      "Console.ReadLine()" + vbCrLf + _
                      "   Console.WriteLine"
For Each match As Match In Regex.Matches(input, pattern, _
                                         RegexOptions.IgnorePatternWhitespace Or RegexOptions.IgnoreCase Or RegexOptions.MultiLine)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'System.Console.Write' found at position 0.
'       'Console.Write' found at position 36.
'       'Console.Write' found at position 61.
'       '   Console.Write' found at position 110.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
^ 入力ストリームの先頭と一致します。
\s* 0 個以上の空白文字と一致します。
(System.)?? 文字列 System. の 0 回または 1 回の出現と一致します。
Console.Write 文字列 Console.Write と一致します。
(Line)?? 文字列 Line の 0 回または 1 回の出現と一致します。
\(?? 左かっこの 0 回または 1 回の出現と一致します。

n 回の繰り返しに一致 (最短一致): {n}?

{n}? 量指定子は、直前の要素の n 回の繰り返しに一致します。ここで、n は任意の整数です。 これは、最長一致の量指定子 {n} に対応する、最短一致の量指定子です。

次の例では、正規表現 \b(\w{3,}?\.){2}?\w{3,}?\b を使用して Web サイトのアドレスを識別します。 式は、www.microsoft.com および msdn.microsoft.com と一致しますが、mywebsite または mycompany.com とは一致しません。

string pattern = @"\b(\w{3,}?\.){2}?\w{3,}?\b";
string input = "www.microsoft.com msdn.microsoft.com mywebsite mycompany.com";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        'www.microsoft.com' found at position 0.
//        'msdn.microsoft.com' found at position 18.
Dim pattern As String = "\b(\w{3,}?\.){2}?\w{3,}?\b"
Dim input As String = "www.microsoft.com msdn.microsoft.com mywebsite mycompany.com"
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'www.microsoft.com' found at position 0.
'       'msdn.microsoft.com' found at position 18.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
(\w{3,}?\.) 後にドット (ピリオド) 文字が続く 3 個以上 (ただし、できるだけ少ない数) の単語文字と一致します。 このパターンが最初のキャプチャ グループです。
(\w{3,}?\.){2}? 最初のグループのパターンと 2 回 (ただし、できるだけ少ない回数) 一致します。
\b ワード境界で照合を終了します。

n 回以上の繰り返しに一致 (最短一致): {n,}?

{n,}? 量指定子は、直前の要素と n 回以上 (ただし、できる限り少ない回数) 一致します。ここで、n は任意の整数です。 これは、最長一致の量指定子 {n,} に対応する、最短一致の量指定子です。

{n}? 量指定子の例については、前のセクションを参照してください。 この例の正規表現は、3 文字以上の後にピリオドが続く文字列に一致させるために {n,} 量指定子を使用しています。

n 回から m 回までの繰り返しに一致 (最短一致): {n,m}?

{n,m}? 量指定子は、直前の要素と n 回から m 回まで (ただし、できるだけ少ない回数) 一致します。ここで、nm は整数です。 これは、最長一致の量指定子 {n,m} に対応する、最短一致の量指定子です。

次の例では、正規表現 \b[A-Z](\w*?\s*?){1,10}[.!?] は、1 個以上 10 個以下の単語が含まれる文と一致します。 18 個の単語が含まれている 1 つの文を除き、入力文字列中のすべての文に一致します。

string pattern = @"\b[A-Z](\w*?\s*?){1,10}[.!?]";
string input = "Hi. I am writing a short note. Its purpose is " +
                      "to test a regular expression that attempts to find " +
                      "sentences with ten or fewer words. Most sentences " +
                      "in this note are short.";
foreach (Match match in Regex.Matches(input, pattern))
   Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);

//  The example displays the following output:
//        'Hi.' found at position 0.
//        'I am writing a short note.' found at position 4.
//        'Most sentences in this note are short.' found at position 132.
Dim pattern As String = "\b[A-Z](\w*\s?){1,10}?[.!?]"
Dim input As String = "Hi. I am writing a short note. Its purpose is " + _
                      "to test a regular expression that attempts to find " + _
                      "sentences with ten or fewer words. Most sentences " + _
                      "in this note are short."
For Each match As Match In Regex.Matches(input, pattern)
    Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
'       'Hi.' found at position 0.
'       'I am writing a short note.' found at position 4.
'       'Most sentences in this note are short.' found at position 132.

正規表現パターンは、次の表に示すように定義されています。

Pattern 説明
\b ワード境界から開始します。
[A-Z] A から Z の大文字と一致します。
(\w*?\s*?) 後に 1 個以上の空白文字が続く 0 個以上の単語文字と (ただし、できるだけ少ない回数) 一致します。 このパターンが最初のキャプチャ グループです。
{1,10} 前のパターンと 1 回以上 10 回以下の回数一致します。
[.!?] 句読点 .!、または ? のいずれかと一致します。

最長一致と最短一致の量指定子

一部の量指定子には、次の 2 つのバージョンがあります。

  • 最長一致バージョン。

    最長一致の量指定子は、要素をできるだけ多く一致させようとします。

  • 最短一致バージョン。

    最短一致の量指定子は、要素をできるだけ少なく一致させようとします。 最長一致の量指定子に ? を追加すると、最短一致の量指定子にすることができます。

クレジット カード番号などの数値の文字列から最後の 4 桁の数字を抽出することを目的とした正規表現について考えてみましょう。 最長一致の * 量指定子を使用するバージョンの正規表現は、\b.*([0-9]{4})\b です。 しかし、2 個の数値が含まれている文字列の場合、この正規表現は、次の例に示すように 2 番目の数値の最後の 4 桁のみと一致します。

string greedyPattern = @"\b.*([0-9]{4})\b";
string input1 = "1112223333 3992991999";
foreach (Match match in Regex.Matches(input1, greedyPattern))
   Console.WriteLine("Account ending in ******{0}.", match.Groups[1].Value);

// The example displays the following output:
//       Account ending in ******1999.
Dim greedyPattern As String = "\b.*([0-9]{4})\b"
Dim input1 As String = "1112223333 3992991999"
For Each match As Match In Regex.Matches(input1, greedypattern)
    Console.WriteLine("Account ending in ******{0}.", match.Groups(1).Value)
Next
' The example displays the following output:
'       Account ending in ******1999.

この正規表現は最初の数値の一致に失敗します。* 量指定子では、直前の要素をできるだけ多く繰り返して文字列全体に一致させようとして、文字列の最後まで一致と見なされるためです。

この動作は、望ましい動作ではありません。 代わりに *? 次の例に示すように、最短一致の量指定子を使用すると、両方の数値から数字を抽出できます。

string lazyPattern = @"\b.*?([0-9]{4})\b";
string input2 = "1112223333 3992991999";
foreach (Match match in Regex.Matches(input2, lazyPattern))
   Console.WriteLine("Account ending in ******{0}.", match.Groups[1].Value);

// The example displays the following output:
//       Account ending in ******3333.
//       Account ending in ******1999.
Dim lazyPattern As String = "\b.*?([0-9]{4})\b"
Dim input2 As String = "1112223333 3992991999"
For Each match As Match In Regex.Matches(input2, lazypattern)
    Console.WriteLine("Account ending in ******{0}.", match.Groups(1).Value)
Next
' The example displays the following output:
'       Account ending in ******3333.
'       Account ending in ******1999.

多くの場合、最長一致と最短一致の量指定子を使用した正規表現は、同じ一致結果を返します。 異なる結果を返すことが最も多いのは、任意の文字と一致するワイルドカード (.) のメタ文字を使用した場合です。

量指定子と空一致

量指定子 *+、および {n,m} と、これらに対応する最短一致の量指定子は、最小回数のキャプチャが見つかった場合、空一致の後には繰り返されません。 この規則により、可能なグループ キャプチャの最大回数が無限またはほぼ無限のときに、量指定子が空の部分式一致の無限ループに入ることを回避できます。

たとえば、次のコードは、0 個または 1 個の文字 a と 0 回以上一致する正規表現パターン (a?)* を使用して Regex.Match メソッドを呼び出した結果を示します。 単一のキャプチャ グループによって各 aString.Empty がキャプチャされますが、2 番目の空の一致はないことに注意してください。これは、最初の空の一致で量指定子が繰り返しを停止するためです。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = "(a?)*";
      string input = "aaabbb";
      Match match = Regex.Match(input, pattern);
      Console.WriteLine("Match: '{0}' at index {1}",
                        match.Value, match.Index);
      if (match.Groups.Count > 1) {
         GroupCollection groups = match.Groups;
         for (int grpCtr = 1; grpCtr <= groups.Count - 1; grpCtr++) {
            Console.WriteLine("   Group {0}: '{1}' at index {2}",
                              grpCtr,
                              groups[grpCtr].Value,
                              groups[grpCtr].Index);
            int captureCtr = 0;
            foreach (Capture capture in groups[grpCtr].Captures) {
               captureCtr++;
               Console.WriteLine("      Capture {0}: '{1}' at index {2}",
                                 captureCtr, capture.Value, capture.Index);
            }
         }
      }
   }
}
// The example displays the following output:
//       Match: 'aaa' at index 0
//          Group 1: '' at index 3
//             Capture 1: 'a' at index 0
//             Capture 2: 'a' at index 1
//             Capture 3: 'a' at index 2
//             Capture 4: '' at index 3
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(a?)*"
        Dim input As String = "aaabbb"
        Dim match As Match = Regex.Match(input, pattern)
        Console.WriteLine("Match: '{0}' at index {1}",
                          match.Value, match.Index)
        If match.Groups.Count > 1 Then
            Dim groups As GroupCollection = match.Groups
            For grpCtr As Integer = 1 To groups.Count - 1
                Console.WriteLine("   Group {0}: '{1}' at index {2}",
                                  grpCtr,
                                  groups(grpCtr).Value,
                                  groups(grpCtr).Index)
                Dim captureCtr As Integer = 0
                For Each capture As Capture In groups(grpCtr).Captures
                    captureCtr += 1
                    Console.WriteLine("      Capture {0}: '{1}' at index {2}",
                                      captureCtr, capture.Value, capture.Index)
                Next
            Next
        End If
    End Sub
End Module
' The example displays the following output:
'       Match: 'aaa' at index 0
'          Group 1: '' at index 3
'             Capture 1: 'a' at index 0
'             Capture 2: 'a' at index 1
'             Capture 3: 'a' at index 2
'             Capture 4: '' at index 3

最小回数と最大回数のキャプチャを定義するキャプチャ グループと固定回数のキャプチャを定義するキャプチャ グループとの間の実際の違いを確認するために、正規表現パターンの (a\1|(?(1)\1)){0,2}(a\1|(?(1)\1)){2} について検討します。 どちらの正規表現も、次の表で定義される単一のキャプチャ グループで構成されています。

Pattern 説明
(a\1 a および最初のキャプチャ グループの値と一致するか、
|(?(1) … または、最初のキャプチャ グループが定義されているかどうかをテストします。 (?(1) コンストラクトでは、キャプチャ グループは定義されません。
\1)) 最初のキャプチャ グループが存在する場合、その値と一致します。 グループが存在しない場合、そのグループは String.Empty と一致します。

最初の正規表現では 0 ~ 2 回、このパターンとの照合が行われます。2 番目の正規表現では、厳密に 2 回です。 最初のパターンは String.Empty の最初のキャプチャでキャプチャの最小回数に達するため、a\1 との照合は繰り返されません。 {0,2} 量指定子では、最後の繰り返しでの空の一致のみが許可されます。 これに対し、2 番目の正規表現は、a\1を 2 回評価するため、a と一致します。 繰り返しの最小回数 2 によって、エンジンは強制的に空の一致の後に繰り返しを行います。

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern, input;

      pattern = @"(a\1|(?(1)\1)){0,2}";
      input = "aaabbb";

      Console.WriteLine("Regex pattern: {0}", pattern);
      Match match = Regex.Match(input, pattern);
      Console.WriteLine("Match: '{0}' at position {1}.",
                        match.Value, match.Index);
      if (match.Groups.Count > 1) {
         for (int groupCtr = 1; groupCtr <= match.Groups.Count - 1; groupCtr++)
         {
            Group group = match.Groups[groupCtr];
            Console.WriteLine("   Group: {0}: '{1}' at position {2}.",
                              groupCtr, group.Value, group.Index);
            int captureCtr = 0;
            foreach (Capture capture in group.Captures) {
               captureCtr++;
               Console.WriteLine("      Capture: {0}: '{1}' at position {2}.",
                                 captureCtr, capture.Value, capture.Index);
            }
         }
      }
      Console.WriteLine();

      pattern = @"(a\1|(?(1)\1)){2}";
      Console.WriteLine("Regex pattern: {0}", pattern);
      match = Regex.Match(input, pattern);
         Console.WriteLine("Matched '{0}' at position {1}.",
                           match.Value, match.Index);
      if (match.Groups.Count > 1) {
         for (int groupCtr = 1; groupCtr <= match.Groups.Count - 1; groupCtr++)
         {
            Group group = match.Groups[groupCtr];
            Console.WriteLine("   Group: {0}: '{1}' at position {2}.",
                              groupCtr, group.Value, group.Index);
            int captureCtr = 0;
            foreach (Capture capture in group.Captures) {
               captureCtr++;
               Console.WriteLine("      Capture: {0}: '{1}' at position {2}.",
                                 captureCtr, capture.Value, capture.Index);
            }
         }
      }
   }
}
// The example displays the following output:
//       Regex pattern: (a\1|(?(1)\1)){0,2}
//       Match: '' at position 0.
//          Group: 1: '' at position 0.
//             Capture: 1: '' at position 0.
//
//       Regex pattern: (a\1|(?(1)\1)){2}
//       Matched 'a' at position 0.
//          Group: 1: 'a' at position 0.
//             Capture: 1: '' at position 0.
//             Capture: 2: 'a' at position 0.
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern, input As String

        pattern = "(a\1|(?(1)\1)){0,2}"
        input = "aaabbb"

        Console.WriteLine("Regex pattern: {0}", pattern)
        Dim match As Match = Regex.Match(input, pattern)
        Console.WriteLine("Match: '{0}' at position {1}.",
                          match.Value, match.Index)
        If match.Groups.Count > 1 Then
            For groupCtr As Integer = 1 To match.Groups.Count - 1
                Dim group As Group = match.Groups(groupCtr)
                Console.WriteLine("   Group: {0}: '{1}' at position {2}.",
                                  groupCtr, group.Value, group.Index)
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    captureCtr += 1
                    Console.WriteLine("      Capture: {0}: '{1}' at position {2}.",
                                      captureCtr, capture.Value, capture.Index)
                Next
            Next
        End If
        Console.WriteLine()

        pattern = "(a\1|(?(1)\1)){2}"
        Console.WriteLine("Regex pattern: {0}", pattern)
        match = Regex.Match(input, pattern)
        Console.WriteLine("Matched '{0}' at position {1}.",
                          match.Value, match.Index)
        If match.Groups.Count > 1 Then
            For groupCtr As Integer = 1 To match.Groups.Count - 1
                Dim group As Group = match.Groups(groupCtr)
                Console.WriteLine("   Group: {0}: '{1}' at position {2}.",
                                  groupCtr, group.Value, group.Index)
                Dim captureCtr As Integer = 0
                For Each capture As Capture In group.Captures
                    captureCtr += 1
                    Console.WriteLine("      Capture: {0}: '{1}' at position {2}.",
                                      captureCtr, capture.Value, capture.Index)
                Next
            Next
        End If
    End Sub
End Module
' The example displays the following output:
'       Regex pattern: (a\1|(?(1)\1)){0,2}
'       Match: '' at position 0.
'          Group: 1: '' at position 0.
'             Capture: 1: '' at position 0.
'       
'       Regex pattern: (a\1|(?(1)\1)){2}
'       Matched 'a' at position 0.
'          Group: 1: 'a' at position 0.
'             Capture: 1: '' at position 0.
'             Capture: 2: 'a' at position 0.

関連項目