Udostępnij za pośrednictwem


Kwantyfikatory w wyrażeniach regularnych

Kwantyfikatory określają, ile wystąpień klasy znaków, grupy lub znaków musi znajdować się w danych wejściowych dla dopasowania. W poniższej tabeli wymieniono kwantyfikatory obsługiwane przez platformę .NET:

Chciwy kwantyfikator Kwantyfikator z opóźnieniem opis
* *? Dopasuje zero lub więcej razy.
+ +? Dopasuj co najmniej jeden raz.
? ?? Dopasuje zero lub jeden raz.
{N} {N}? Pasuje dokładnie n razy.
{N,} {N,}? Pasuje co najmniej n razy.
{n,m} {n,m}? Dopasowania od n do m razy.

Ilości n i m są stałymi liczbami całkowitymi. Zazwyczaj kwantyfikatory są chciwy. Powodują one dopasowanie aparatu wyrażeń regularnych do jak największej liczby wystąpień określonych wzorców. Dołączanie ? znaku do kwantyfikatora sprawia, że jest leniwe. Powoduje, że aparat wyrażeń regularnych jest zgodny z jak najmniejszą liczbą wystąpień. Pełny opis różnicy między chciwością i leniwymi kwantyfikatorów można znaleźć w sekcji Greedy i Lazy Quantifiers w dalszej części tego artykułu.

Ważne

Zagnieżdżanie kwantyfikatorów, takich jak wzorzec (a*)*wyrażenia regularnego, może zwiększyć liczbę porównań, które musi wykonać aparat wyrażeń regularnych. Liczba porównań może wzrosnąć jako funkcja wykładnicza liczby znaków w ciągu wejściowym. Aby uzyskać więcej informacji na temat tego zachowania i jego obejść, zobacz Wycofywanie.

Kwantyfikatory wyrażeń regularnych

W poniższych sekcjach wymieniono kwantyfikatory obsługiwane przez wyrażenia regularne platformy .NET:

Uwaga

Jeśli znaki *, +, ?, {i } występują we wzorcu wyrażenia regularnego, aparat wyrażeń regularnych interpretuje je jako kwantyfikatory lub część konstrukcji kwantyfikatora, chyba że zostaną one uwzględnione w klasie znaków. Aby interpretować je jako znaki literału poza klasą znaków, należy je usunąć, poprzedzając je ukośnikiem odwrotnym. Na przykład ciąg \* we wzorcu wyrażenia regularnego jest interpretowany jako znak gwiazdki literału ("*").

Dopasuj zero lub więcej razy: *

Kwantyfikator * pasuje do poprzedniego elementu zero lub więcej razy. Odpowiada kwantyfikatorowi {0,} . * jest chciwym kwantyfikatorem, którego leniwy odpowiednik to *?.

Poniższy przykład ilustruje to wyrażenie regularne. Pięć z dziewięciu grup cyfr w ciągu wejściowym pasuje do wzorca, a cztery (95, 929, 9219i 9919) nie.

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Określa, że dopasowanie musi rozpoczynać się od granicy słowa.
91* Dopasuje znak 9 zero lub więcej 1 znaków.
9* Dopasuje zero lub więcej 9 znaków.
\b Określa, że dopasowanie musi kończyć się granicą słowa.

Dopasuj co najmniej jeden raz: +

Kwantyfikator + pasuje do poprzedniego elementu co najmniej raz. Jest to odpowiednik {1,}elementu . + jest chciwym kwantyfikatorem, którego leniwy odpowiednik to +?.

Na przykład wyrażenie \ban+\w*?\b regularne próbuje dopasować całe wyrazy rozpoczynające się literą a , po której następuje co najmniej jedno wystąpienie litery n. Poniższy przykład ilustruje to wyrażenie regularne. Wyrażenie regularne pasuje do wyrazów an, , announcementannuali antique, i poprawnie nie można dopasować autumn wartości i all.

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
an+ Dopasuje znak z a co najmniej jednym n znakiem.
\w*? Dopasuje znak słowa zero lub więcej razy, ale jak najwięcej razy.
\b Kończy na granicy wyrazu.

Dopasuj zero lub jeden raz: ?

Kwantyfikator ? pasuje do poprzedniego elementu zero lub jeden raz. Jest to odpowiednik {0,1}elementu . ? jest chciwym kwantyfikatorem, którego leniwy odpowiednik to ??.

Na przykład wyrażenie \ban?\b regularne próbuje dopasować całe wyrazy rozpoczynające się literą a , po której następuje zero lub jedno wystąpienie litery n. Innymi słowy, próbuje dopasować wyrazy a i an. Poniższy przykład ilustruje to wyrażenie regularne:

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
an? Pasuje do zera a lub jednego n znaku.
\b Kończy na granicy wyrazu.

Dopasuj dokładnie n razy: {n}

{N} kwantyfikator pasuje do poprzedniego elementu dokładnie n razy, gdzie n jest dowolną liczbą całkowitą. {n jest chciwym kwantyfikatorem, którego leniwy odpowiednik to{ n}?.}

Na przykład wyrażenie \b\d+\,\d{3}\b regularne próbuje dopasować granicę słowa, po której następuje co najmniej jedna cyfra dziesiętna, po której następuje trzy cyfry dziesiętne, po których następuje granica słowa. Poniższy przykład ilustruje to wyrażenie regularne:

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
\d+ Dopasuje co najmniej jedną cyfrę dziesiętną.
\, Dopasuje znak przecinka.
\d{3} Pasuje do trzech cyfr dziesiętnych.
\b Kończy na granicy wyrazu.

Dopasuj co najmniej n razy: {n,}

{N,} kwantyfikator pasuje do poprzedniego elementu co najmniej n razy, gdzie n jest dowolną liczbą całkowitą. {n jest chciwym kwantyfikatorem, którego leniwy odpowiednik to{ n,}?.,}

Na przykład wyrażenie \b\d{2,}\b\D+ regularne próbuje dopasować granicę słowa, po której następuje co najmniej dwie cyfry, po których następuje granica słowa i znak niecyfrowy. Poniższy przykład ilustruje to wyrażenie regularne. Wyrażenie regularne nie pasuje do frazy "7 days" , ponieważ zawiera tylko jedną cyfrę dziesiętną, ale pomyślnie pasuje do fraz "10 weeks" i "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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
\d{2,} Dopasuje co najmniej dwie cyfry dziesiętne.
\b Pasuje do granicy słowa.
\D+ Dopasuje co najmniej jedną cyfrę inną niż dziesiętna.

Dopasowanie między n i m razy: {n,m}

Kwantyfikator {n,m} pasuje do poprzedniego elementu co najmniej n razy, ale nie więcej niż m razy, gdzie n i m są liczbami całkowitymi. {n,m} to chciwy kwantyfikator, którego leniwy odpowiednik to{ n,m.}?

W poniższym przykładzie wyrażenie (00\s){2,4} regularne próbuje dopasować się między dwoma i czterema wystąpieniami dwóch cyfr zero, po których następuje spacja. Ostatnia część ciągu wejściowego zawiera ten wzorzec pięć razy, a nie maksymalnie cztery. Jednak tylko początkowa część tego podciągu (do spacji i piątej pary zer) jest zgodna ze wzorcem wyrażenia regularnego.

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.

Dopasuj zero lub więcej razy (z opóźnieniem): *?

Kwantyfikator *? pasuje do poprzedniego elementu zero lub więcej razy, ale jak najszybciej. To leniwy odpowiednik chciwego kwantyfikatora *.

W poniższym przykładzie wyrażenie \b\w*?oo\w*?\b regularne pasuje do wszystkich wyrazów zawierających ciąg 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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
\w*? Dopasuje zero lub więcej znaków słów, ale jak najmniej znaków.
oo Pasuje do ciągu oo.
\w*? Dopasuje zero lub więcej znaków słów, ale jak najmniej znaków.
\b Koniec na granicy wyrazu.

Dopasuj co najmniej jeden raz (z opóźnieniem): +?

Kwantyfikator +? pasuje do poprzedniego elementu co najmniej raz, ale jak najwięcej razy. To leniwy odpowiednik chciwego kwantyfikatora +.

Na przykład wyrażenie \b\w+?\b regularne pasuje do jednego lub większej liczby znaków rozdzielonych granicami wyrazów. Poniższy przykład ilustruje to wyrażenie regularne:

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.

Dopasuj zero lub jeden raz (z opóźnieniem): ??

Kwantyfikator ?? pasuje do poprzedniego elementu zero lub jeden raz, ale jak najszybciej. To leniwy odpowiednik chciwego kwantyfikatora ?.

Na przykład wyrażenie ^\s*(System.)??Console.Write(Line)??\(?? regularne próbuje dopasować ciągi Console.Write lub Console.WriteLine. Ciąg może również zawierać System. wartość przed Consoleciągiem , a następnie nawias otwierający. Ciąg musi znajdować się na początku wiersza, chociaż może być poprzedzony białym znakiem. Poniższy przykład ilustruje to wyrażenie regularne:

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
^ Pasuje do początku strumienia wejściowego.
\s* Dopasuje zero lub więcej znaków odstępu.
(System.)?? Dopasuje zero lub jedno wystąpienie ciągu System..
Console.Write Pasuje do ciągu Console.Write.
(Line)?? Dopasuje zero lub jedno wystąpienie ciągu Line.
\(?? Pasuje do zera lub jednego wystąpienia nawiasu otwierającego.

Dopasuj dokładnie n razy (z opóźnieniem): {n}?

N {}? kwantyfikator pasuje dokładnie n do poprzedniego elementu, gdzie n jest dowolną liczbą całkowitą. To leniwy odpowiednik chciwego kwantyfikatora {n}.

W poniższym przykładzie wyrażenie \b(\w{3,}?\.){2}?\w{3,}?\b regularne służy do identyfikowania adresu witryny internetowej. Wyrażenie jest zgodne www.microsoft.com i msdn.microsoft.com ale nie jest zgodne mywebsite lub 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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
(\w{3,}?\.) Dopasuje co najmniej trzy znaki wyrazów, ale jak najwięcej znaków, po których następuje znak kropki lub kropki. Ten wzorzec jest pierwszą grupą przechwytywania.
(\w{3,}?\.){2}? Dopasuj wzorzec w pierwszej grupie dwa razy, ale jak najwięcej razy.
\b Zakończ dopasowanie na granicy wyrazu.

Dopasuj co najmniej n razy (z opóźnieniem): {n,}?

{N,}? kwantyfikator pasuje do poprzedniego elementu co najmniej n raz, gdzie n jest dowolną liczbą całkowitą, ale jak najszybciej. To leniwy odpowiednik chciwego kwantyfikatora {n,}.

Zobacz przykład {dla kwantyfikatora n}? w poprzedniej sekcji, aby zapoznać się z ilustracją. Wyrażenie regularne w tym przykładzie używa {kwantyfikatora n,} do dopasowania ciągu, który ma co najmniej trzy znaki, po którym następuje kropka.

Dopasowanie między n i m razy (z opóźnieniem): {n,m}?

Kwantyfikator {n,m}? pasuje do poprzedniego elementu między n im, gdzie n i m są liczbami całkowitymi, ale jak najwięcej razy. To leniwy odpowiednik chciwego kwantyfikatora {n,m.}

W poniższym przykładzie wyrażenie \b[A-Z](\w*?\s*?){1,10}[.!?] regularne pasuje do zdań zawierających od 1 do 10 wyrazów. Pasuje do wszystkich zdań w ciągu wejściowym z wyjątkiem jednego zdania zawierającego 18 słów.

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.

Wzorzec wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli:

Wzorzec opis
\b Rozpoczyna na granicy wyrazu.
[A-Z] Dopasuje wielkie litery od A do Z.
(\w*?\s*?) Dopasuje zero lub więcej znaków słów, po których następuje co najmniej jeden znak odstępu, ale jak najszybciej. Ten wzorzec jest pierwszą grupą przechwytywania.
{1,10} Pasuje do poprzedniego wzorca z zakresu od 1 do 10 razy.
[.!?] Dopasuje dowolny z znaków .interpunkcyjnych , !lub ?.

Chciwy i leniwy kwantyfikatory

Niektóre kwantyfikatory mają dwie wersje:

  • Chciwa wersja.

    Chciwy kwantyfikator próbuje dopasować element jak najwięcej razy.

  • Niechętna (lub leniwa) wersja.

    Kwantyfikator niechłanny próbuje dopasować element tak mało razy, jak to możliwe. Możesz przekształcić chciwy kwantyfikator w leniwy kwantyfikator, dodając ?element .

Rozważ wyrażenie regularne, które ma wyodrębnić ostatnie cztery cyfry z ciągu liczb, takiego jak numer karty kredytowej. Wersja wyrażenia regularnego używająca chciwego * kwantyfikatora to \b.*([0-9]{4})\b. Jeśli jednak ciąg zawiera dwie liczby, to wyrażenie regularne pasuje tylko do czterech ostatnich cyfr drugiej liczby, jak pokazano w poniższym przykładzie:

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.

Wyrażenie regularne nie jest zgodne z pierwszą liczbą, ponieważ * kwantyfikator próbuje dopasować poprzedni element tyle razy, ile to możliwe w całym ciągu, a więc znajduje dopasowanie na końcu ciągu.

To zachowanie nie jest pożądane. Zamiast tego możesz użyć kwantyfikatora z opóźnieniem *? , aby wyodrębnić cyfry z obu liczb, jak pokazano w poniższym przykładzie:

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.

W większości przypadków wyrażenia regularne z chciwością i leniwymi kwantyfikatory zwracają te same dopasowania. Najczęściej zwracają różne wyniki, gdy są używane z metacharacterem z symbolem wieloznacznymi (.), który pasuje do dowolnego znaku.

Kwantyfikatory i puste dopasowania

Kwantyfikatory , i {n m}, i ich leniwe odpowiedniki nigdy nie powtarzają się po pustym dopasowaniu, gdy zostanie znaleziona minimalna liczba przechwytów.+* Ta reguła uniemożliwia kwantyfikatory wprowadzania nieskończonych pętli na pustym podwyrażeniu, gdy maksymalna liczba możliwych przechwytywania grup jest nieskończona lub prawie nieskończona.

Na przykład poniższy kod przedstawia wynik wywołania metody ze wzorcem (a?)*wyrażenia regularnego, który pasuje do Regex.Match zera lub jednego a znaku zero lub więcej razy. Pojedyncza grupa przechwytywania przechwytuje każdy a element i String.Empty, ale nie ma drugiego pustego dopasowania, ponieważ pierwsze puste dopasowanie powoduje, że kwantyfikator przestaje powtarzać.

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

Aby zobaczyć praktyczną różnicę między grupą przechwytywania, która definiuje minimalną i maksymalną liczbę przechwytywania oraz taką, która definiuje stałą liczbę przechwytywania, należy wziąć pod uwagę wzorce (a\1|(?(1)\1)){0,2} wyrażeń regularnych i (a\1|(?(1)\1)){2}. Oba wyrażenia regularne składają się z pojedynczej grupy przechwytywania, która jest zdefiniowana w poniższej tabeli:

Wzorzec opis
(a\1 Albo pasuje a wraz z wartością pierwszej przechwyconej grupy ...
|(?(1) ... lub sprawdza, czy zdefiniowano pierwszą przechwyconą grupę. Konstrukcja (?(1) nie definiuje grupy przechwytywania.
\1)) Jeśli istnieje pierwsza przechwycona grupa, dopasuj jej wartość. Jeśli grupa nie istnieje, grupa będzie zgodna z String.Empty.

Pierwsze wyrażenie regularne próbuje dopasować ten wzorzec od zera do dwóch razy; drugi, dokładnie dwa razy. Ponieważ pierwszy wzorzec osiąga minimalną liczbę przechwyconych przy użyciu pierwszego przechwycenia String.Emptyelementu , nigdy nie powtarza się, aby spróbować dopasować a\1element . Kwantyfikator {0,2} umożliwia tylko puste dopasowania w ostatniej iteracji. Natomiast drugie wyrażenie regularne jest zgodne a , ponieważ ocenia a\1 je po raz drugi. Minimalna liczba iteracji, 2, wymusza powtórzenie aparatu po pustym dopasowaniu.

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.

Zobacz też