Udostępnij za pośrednictwem


Szczegóły dotyczące działania wyrażeń regularnych

Silnik wyrażeń regularnych .NET to dopasowywacz wyrażeń regularnych oparty na wycofywaniu, który zawiera tradycyjny aparat nieskończonych automatów nieokreślonych (NFA), taki jak używany przez Perl, Python, Emacs i Tcl. To odróżnia go od szybszych, ale bardziej ograniczonych czystych silników deterministycznych automatów skończonych (DFA), takich jak te występujące w awk, egrep lub lex. Różni się to również od ustandaryzowanych, ale wolniejszych, POSIX NFAs. W poniższej sekcji opisano trzy typy aparatów wyrażeń regularnych i wyjaśniono, dlaczego wyrażenia regularne na platformie .NET są implementowane przy użyciu tradycyjnego aparatu NFA.

Zalety aparatu NFA

Gdy aparaty DFA wykonują dopasowywanie wzorców, ich kolejność przetwarzania jest sterowana ciągiem wejściowym. Silnik zaczyna od początku ciągu wejściowego i przechodzi sekwencyjnie, aby określić, czy następny znak jest zgodny ze wzorcem wyrażenia regularnego. Mogą zagwarantować dopasowanie najdłuższego możliwego ciągu. Ponieważ nigdy nie testują tego samego znaku dwa razy, silniki DFA nie obsługują przeszukiwania wstecz. Jednak ze względu na to, że aparat DFA zawiera tylko stan skończony, nie może dopasować wzorca z odwołaniami wstecznymi i ponieważ nie tworzy jawnego rozszerzenia, nie może przechwytywać podwyrażeń.

W przeciwieństwie do silników DFA, gdy tradycyjne silniki NFA wykonują dopasowywanie wzorców, ich kolejność przetwarzania jest determinowana przez wzorzec wyrażenia regularnego. W miarę przetwarzania określonego elementu języka, silnik używa chciwego dopasowania; oznacza to, że dopasowuje jak najwięcej z ciągu wejściowego, jak to tylko możliwe. Ale również zapisuje swój stan po pomyślnym dopasowaniu podexpressionu. Jeśli dopasowanie ostatecznie zakończy się niepowodzeniem, silnik może powrócić do stanu zapisanego, aby można było spróbować dodatkowych dopasowań. Ten proces porzucania pomyślnego dopasowania podwyrażenia, tak aby późniejsze elementy wyrażenia regularnego mogły być również zgodne, nazywa się wycofywaniem. Silniki NFA stosują algorytm wycofywania do sprawdzania wszystkich możliwych rozszerzeń wyrażenia regularnego w określonej kolejności i zaakceptowania pierwszego zgodnego dopasowania. Ponieważ tradycyjny automat NFA tworzy określone rozszerzenie wyrażenia regularnego dla pomyślnego dopasowania, może przechwytywać dopasowania podwyrażeń i odwołania wsteczne. Jednak ze względu na to, że tradycyjne NFA wykonuje backtracking, może odwiedzać ten sam stan wiele razy, jeśli dociera do tego stanu różnymi ścieżkami. W rezultacie może działać wykładniczo powoli w najgorszym przypadku. Ponieważ tradycyjny mechanizm NFA akceptuje pierwsze znalezione dopasowanie, może również pozostawić pozostałe (prawdopodobnie dłuższe) dopasowania nieodkrytych.

Silniki NFA POSIX są podobne do tradycyjnych silników NFA, z tą różnicą, że kontynuują wracanie, dopóki nie mogą zagwarantować, że znalazły najdłuższe możliwe dopasowanie. W rezultacie aparat NFA POSIX jest wolniejszy niż tradycyjny aparat NFA, a gdy używasz aparatu NFA POSIX, nie można faworyzować krótszego dopasowania nad dłuższym, zmieniając kolejność przeszukiwania z wycofaniem.

Tradycyjne silniki NFA są preferowane przez programistów, ponieważ oferują większą kontrolę nad dopasowaniem ciągów niż silniki NFA, DFA lub POSIX. Chociaż w najgorszym przypadku mogą działać powoli, można kierować je w celu znalezienia dopasowań w czasie liniowym lub wielomianowym, używając wzorców, które zmniejszają niejednoznaczności i ograniczają wycofywanie. Innymi słowy, chociaż silniki NFA poświęcają wydajność dla mocniejszej funkcjonalności i elastyczności, w większości przypadków oferują one od dobrej do akceptowalnej wydajności, jeśli wyrażenie regularne jest dobrze zbudowane i unika przypadków, w których mechanizm wycofywania drastycznie obniża wydajność.

Uwaga

Aby uzyskać informacje na temat kary wydajnościowej spowodowanej przez nadmierne wycofywanie i sposobów tworzenia wyrażeń regularnych, aby je obejść, zobacz Wycofywanie.

Możliwości silnika .NET

Aby skorzystać z zalet tradycyjnego aparatu NFA, aparat wyrażeń regularnych platformy .NET zawiera kompletny zestaw konstrukcji, aby umożliwić programistom sterowanie mechanizmem backtrackingu. Te konstrukty mogą służyć do szybszego znajdowania dopasowań lub preferowania określonych rozszerzeń ponad innymi.

Inne funkcje aparatu wyrażeń regularnych platformy .NET obejmują następujące elementy:

  • Kwantyfikatory z opóźnieniem: ??, *?, +?, {n,m}?. Te konstrukcje informują mechanizm przeszukiwania wstecznego, aby najpierw przeszukał minimalną liczbę powtórzeń. Natomiast zwykłe chciwe kwantyfikatory próbują najpierw dopasować maksymalną liczbę powtórzeń. Poniższy przykład ilustruje różnicę między nimi. Wyrażenie regularne pasuje do zdania kończącego się liczbą, a grupa wychwytująca ma na celu wyodrębnienie tej liczby. Wyrażenie .+(\d+)\. regularne zawiera chciwy kwantyfikator .+, co powoduje, że aparat wyrażeń regularnych przechwytuje tylko ostatnią cyfrę liczby. Natomiast wyrażenie regularne .+?(\d+)\. zawiera leniwy kwantyfikator .+?, sprawiając, że aparat wyrażeń regularnych przechwytuje całość liczby.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string greedyPattern = @".+(\d+)\.";
            string lazyPattern = @".+?(\d+)\.";
            string input = "This sentence ends with the number 107325.";
            Match match;
    
            // Match using greedy quantifier .+.
            match = Regex.Match(input, greedyPattern);
            if (match.Success)
                Console.WriteLine($"Number at end of sentence (greedy): {match.Groups[1].Value}");
            else
                Console.WriteLine($"{greedyPattern} finds no match.");
    
            // Match using lazy quantifier .+?.
            match = Regex.Match(input, lazyPattern);
            if (match.Success)
                Console.WriteLine($"Number at end of sentence (lazy): {match.Groups[1].Value}");
            else
                Console.WriteLine($"{lazyPattern} finds no match.");
        }
    }
    // The example displays the following output:
    //       Number at end of sentence (greedy): 5
    //       Number at end of sentence (lazy): 107325
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim greedyPattern As String = ".+(\d+)\."
            Dim lazyPattern As String = ".+?(\d+)\."
            Dim input As String = "This sentence ends with the number 107325."
            Dim match As Match
    
            ' Match using greedy quantifier .+.
            match = Regex.Match(input, greedyPattern)
            If match.Success Then
                Console.WriteLine("Number at end of sentence (greedy): {0}",
                                  match.Groups(1).Value)
            Else
                Console.WriteLine("{0} finds no match.", greedyPattern)
            End If
    
            ' Match using lazy quantifier .+?.
            match = Regex.Match(input, lazyPattern)
            If match.Success Then
                Console.WriteLine("Number at end of sentence (lazy): {0}",
                                  match.Groups(1).Value)
            Else
                Console.WriteLine("{0} finds no match.", lazyPattern)
            End If
        End Sub
    End Module
    ' The example displays the following output:
    '       Number at end of sentence (greedy): 5
    '       Number at end of sentence (lazy): 107325
    

    Chciwe i leniwe wersje tego wyrażenia regularnego są zdefiniowane, jak pokazano w poniższej tabeli:

    Wzorzec opis
    .+ (chciwy kwantyfikator) Dopasuj co najmniej jedno wystąpienie dowolnego znaku. Powoduje to, że aparat wyrażeń regularnych dopasowuje cały ciąg, a następnie cofa się zgodnie z potrzebami, aby dopasować pozostałą część wzorca.
    .+? (leniwy kwantyfikator) Dopasuj co najmniej jedno wystąpienie dowolnego znaku, ale dopasuj ich jak najmniej.
    (\d+) Dopasuj co najmniej jeden znak liczbowy i przypisz go do pierwszej grupy wyłapywania.
    \. Dopasuj okres.

    Aby uzyskać więcej informacji na temat leniwych kwantyfikatorów, zobacz Quantifiers.

  • Pozytywne przyszłe spojrzenie: (?=podwyrażenie). Ta funkcja umożliwia aparatowi wycofywania powrót do tego samego miejsca w tekście po dopasowaniu podexpressionu. Przydatne jest wyszukiwanie w całym tekście przez zweryfikowanie wielu wzorców, które zaczynają się od tej samej pozycji. Umożliwia również aparatowi sprawdzenie, czy ciąg znaków istnieje na końcu dopasowania bez uwzględniania ciągu znaków w dopasowanym tekście. W poniższym przykładzie użyto pozytywnego spojrzenia, aby wyodrębnić wyrazy w zdaniu, które nie są zgodne z symbolami interpunkcyjnymi.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string pattern = @"\b[A-Z]+\b(?=\P{P})";
            string input = "If so, what comes next?";
            foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
                Console.WriteLine(match.Value);
        }
    }
    // The example displays the following output:
    //       If
    //       what
    //       comes
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim pattern As String = "\b[A-Z]+\b(?=\P{P})"
            Dim input As String = "If so, what comes next?"
            For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
                Console.WriteLine(match.Value)
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       If
    '       what
    '       comes
    

    Wyrażenie \b[A-Z]+\b(?=\P{P}) regularne jest definiowane, jak pokazano w poniższej tabeli.

    Wzorzec opis
    \b Rozpoczyna się dopasowanie na granicy wyrazu.
    [A-Z]+ Dopasuj dowolny znak alfabetyczny co najmniej raz. Ponieważ metoda Regex.Matches jest wywoływana z opcją RegexOptions.IgnoreCase, porównanie odbywa się bez uwzględniania wielkości liter.
    \b Kończ mecz na granicy wyrazu.
    (?=\P{P}) Przyjrzyj się, czy następny znak jest symbolem interpunkcyjnym. Jeśli tak nie jest, dopasowanie zakończy się pomyślnie.

    Aby uzyskać więcej informacji na temat pozytywnych asercji "lookahead", zobacz Konstrukcje grupowania.

  • Negatywne spojrzenie: (?!podwyrażenie Ta funkcja dodaje możliwość dopasowania wyrażenia tylko wtedy, gdy wyrażenie podrzędne nie jest zgodne. Jest to potężne narzędzie do ograniczania wyszukiwania, ponieważ często łatwiej jest wyeliminować przypadek, niż określić, które przypadki należy uwzględnić. Na przykład trudno jest napisać wyrażenie dla wyrazów, które nie zaczynają się od "non". W poniższym przykładzie użyto negatywnego spojrzenia, aby je wykluczyć.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string pattern = @"\b(?!non)\w+\b";
            string input = "Nonsense is not always non-functional.";
            foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
                Console.WriteLine(match.Value);
        }
    }
    // The example displays the following output:
    //       is
    //       not
    //       always
    //       functional
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim pattern As String = "\b(?!non)\w+\b"
            Dim input As String = "Nonsense is not always non-functional."
            For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
                Console.WriteLine(match.Value)
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       is
    '       not
    '       always
    '       functional
    

    Wzorzec \b(?!non)\w+\b wyrażenia regularnego jest zdefiniowany, jak pokazano w poniższej tabeli.

    Wzorzec opis
    \b Rozpoczyna się dopasowanie na granicy wyrazu.
    (?!non) Spójrz dalej, aby upewnić się, że bieżący ciąg nie zaczyna się od "non". Jeśli tak, dopasowanie zakończy się niepowodzeniem.
    (\w+) Dopasowuje co najmniej jeden znak słowa.
    \b Kończ mecz na granicy wyrazu.

    Aby uzyskać więcej informacji na temat negatywnych asercji lookahead, zobacz Konstrukcje grupowania.

  • Ocena warunkowa: (?(wyrażenie)tak|nie) i (?(nazwa)tak|nie), gdzie wyrażenie jest podwyrażeniem do dopasowania, nazwa jest nazwą grupy przechwytywania, tak jest ciągiem do dopasowania, jeśli wyrażenie jest dopasowane lub nazwa jest prawidłową, niepustą przechwyconą grupą, a nie jest podwyrażeniem do dopasowania, jeśli wyrażenie nie jest dopasowane lub nazwa nie jest prawidłową, niepustą grupą przechwyconą. Ta funkcja umożliwia silnikowi wyszukiwanie przy użyciu więcej niż jednego alternatywnego wzorca, w zależności od wyniku poprzedniego podwyrażenia lub wyniku asercji o zerowej szerokości. Pozwala to na bardziej zaawansowaną formę wnioskowania wstecznego, która pozwala na przykład dopasowanie podwyrażenia na podstawie tego, czy poprzednie podwyrażenie zostało dopasowane. Wyrażenie regularne w poniższym przykładzie pasuje do akapitów przeznaczonych zarówno do użytku publicznego, jak i wewnętrznego. Akapity przeznaczone tylko do użytku wewnętrznego zaczynają się od tagu <PRIVATE> . Wzorzec wyrażenia regularnego ^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$ wykorzystuje ocenę warunkową do przypisania zawartości akapitów przeznaczonych do użytku publicznego i wewnętrznego do oddzielnych grup przechwytywania. Te akapity można następnie traktować inaczej.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string input = "<PRIVATE> This is not for public consumption." + Environment.NewLine +
                           "But this is for public consumption." + Environment.NewLine +
                           "<PRIVATE> Again, this is confidential.\n";
            string pattern = @"^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$";
            string publicDocument = null, privateDocument = null;
    
            foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Multiline))
            {
                if (match.Groups[1].Success)
                {
                    privateDocument += match.Groups[1].Value + "\n";
                }
                else
                {
                    publicDocument += match.Groups[3].Value + "\n";
                    privateDocument += match.Groups[3].Value + "\n";
                }
            }
    
            Console.WriteLine("Private Document:");
            Console.WriteLine(privateDocument);
            Console.WriteLine("Public Document:");
            Console.WriteLine(publicDocument);
        }
    }
    // The example displays the following output:
    //    Private Document:
    //    This is not for public consumption.
    //    But this is for public consumption.
    //    Again, this is confidential.
    //
    //    Public Document:
    //    But this is for public consumption.
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim input As String = "<PRIVATE> This is not for public consumption." + vbCrLf + _
                                  "But this is for public consumption." + vbCrLf + _
                                  "<PRIVATE> Again, this is confidential." + vbCrLf
            Dim pattern As String = "^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$"
            Dim publicDocument As String = Nothing
            Dim privateDocument As String = Nothing
    
            For Each match As Match In Regex.Matches(input, pattern, RegexOptions.Multiline)
                If match.Groups(1).Success Then
                    privateDocument += match.Groups(1).Value + vbCrLf
                Else
                    publicDocument += match.Groups(3).Value + vbCrLf
                    privateDocument += match.Groups(3).Value + vbCrLf
                End If
            Next
    
            Console.WriteLine("Private Document:")
            Console.WriteLine(privateDocument)
            Console.WriteLine("Public Document:")
            Console.WriteLine(publicDocument)
        End Sub
    End Module
    ' The example displays the following output:
    '    Private Document:
    '    This is not for public consumption.
    '    But this is for public consumption.
    '    Again, this is confidential.
    '    
    '    Public Document:
    '    But this is for public consumption.
    

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

    Wzorzec opis
    ^ Rozpocznij dopasowanie od początku wiersza.
    (?<Pvt>\<PRIVATE\>\s)? Dopasuj zero lub jedno wystąpienie ciągu <PRIVATE>, po którym następuje biały znak. Przypisz dopasowanie do grupy przechwytywania o nazwie Pvt.
    (?(Pvt)((\w+\p{P}?\s)+) Pvt Jeśli grupa przechwytywania istnieje, dopasuj jedno lub więcej wystąpień jednego lub więcej znaków słowa, po czym następuje zero lub jeden separator interpunkcyjny, a następnie znak odstępu. Przypisz podciąg do pierwszej grupy przechwytywania.
    |((\w+\p{P}?\s)+)) Jeśli grupa przechwytywania nie istnieje, dopasuj jedno lub więcej wystąpień jednego lub więcej znaków składających się na słowo, po których następuje zero lub jeden znak interpunkcyjny, a potem znak odstępu. Przypisz podciąg do trzeciej grupy przechwytywania.
    \r?$ Dopasuj koniec wiersza lub końca ciągu.

    Aby uzyskać więcej informacji na temat oceny warunkowej, zobacz Konstrukcje alternatywne.

  • Równoważenie definicji grup: (?<name1-name2>podwyrażenie). Ta funkcja umożliwia aparatowi wyrażeń regularnych śledzenie zagnieżdżonych konstrukcji, takich jak nawiasy lub nawiasy otwierające i zamykające. Przykład można znaleźć w temacie Konstrukcje grupowania.

  • Grupy niepodzielne: (?>podwyrażenie). Ta funkcja pozwala silnikowi backtrackingu zagwarantować, że podwyrażenie jest zgodne tylko z pierwszym dopasowaniem znalezionym dla tego podwyrażenia, tak jakby było uruchamiane niezależnie od wyrażenia, które je zawiera. Jeśli nie używasz tej konstrukcji, wycofywanie wyszukiwań z większego wyrażenia może zmienić zachowanie podexpressionu. Na przykład wyrażenie regularne (a+)\w dopasowuje co najmniej jeden znak "a" razem ze znakiem słowa, który następuje po sekwencji znaków "a", i przypisuje je do pierwszej grupy przechwytywania. Jeśli jednak końcowy znak ciągu wejściowego jest również "a", pasuje do elementu językowego \w i nie jest uwzględniony w przechwyconej grupie.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string[] inputs = { "aaaaa", "aaaaab" };
            string backtrackingPattern = @"(a+)\w";
            Match match;
    
            foreach (string input in inputs)
            {
                Console.WriteLine($"Input: {input}");
                match = Regex.Match(input, backtrackingPattern);
                Console.WriteLine($"   Pattern: {backtrackingPattern}");
                if (match.Success)
                {
                    Console.WriteLine($"      Match: {match.Value}");
                    Console.WriteLine($"      Group 1: {match.Groups[1].Value}");
                }
                else
                {
                    Console.WriteLine("      Match failed.");
                }
            }
            Console.WriteLine();
        }
    }
    // The example displays the following output:
    //       Input: aaaaa
    //          Pattern: (a+)\w
    //             Match: aaaaa
    //             Group 1: aaaa
    //       Input: aaaaab
    //          Pattern: (a+)\w
    //             Match: aaaaab
    //             Group 1: aaaaa
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim inputs() As String = {"aaaaa", "aaaaab"}
            Dim backtrackingPattern As String = "(a+)\w"
            Dim match As Match
    
            For Each input As String In inputs
                Console.WriteLine("Input: {0}", input)
                match = Regex.Match(input, backtrackingPattern)
                Console.WriteLine("   Pattern: {0}", backtrackingPattern)
                If match.Success Then
                    Console.WriteLine("      Match: {0}", match.Value)
                    Console.WriteLine("      Group 1: {0}", match.Groups(1).Value)
                Else
                    Console.WriteLine("      Match failed.")
                End If
            Next
            Console.WriteLine()
        End Sub
    End Module
    ' The example displays the following output:
    '       Input: aaaaa
    '          Pattern: (a+)\w
    '             Match: aaaaa
    '             Group 1: aaaa
    '       Input: aaaaab
    '          Pattern: (a+)\w
    '             Match: aaaaab
    '             Group 1: aaaaa
    

    Wyrażenie regularne ((?>a+))\w uniemożliwia to zachowanie. Ponieważ wszystkie kolejne litery "a" są dopasowywane bez cofania się, pierwsza grupa przechwytywania zawiera wszystkie następujące po sobie litery "a". Jeśli znaki "a" nie są następowane przez przynajmniej jeden inny znak niż "a", dopasowanie kończy się niepowodzeniem.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string[] inputs = { "aaaaa", "aaaaab" };
            string nonbacktrackingPattern = @"((?>a+))\w";
            Match match;
    
            foreach (string input in inputs)
            {
                Console.WriteLine($"Input: {input}");
                match = Regex.Match(input, nonbacktrackingPattern);
                Console.WriteLine($"   Pattern: {nonbacktrackingPattern}");
                if (match.Success)
                {
                    Console.WriteLine($"      Match: {match.Value}");
                    Console.WriteLine($"      Group 1: {match.Groups[1].Value}");
                }
                else
                {
                    Console.WriteLine("      Match failed.");
                }
            }
            Console.WriteLine();
        }
    }
    // The example displays the following output:
    //       Input: aaaaa
    //          Pattern: ((?>a+))\w
    //             Match failed.
    //       Input: aaaaab
    //          Pattern: ((?>a+))\w
    //             Match: aaaaab
    //             Group 1: aaaaa
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim inputs() As String = {"aaaaa", "aaaaab"}
            Dim nonbacktrackingPattern As String = "((?>a+))\w"
            Dim match As Match
    
            For Each input As String In inputs
                Console.WriteLine("Input: {0}", input)
                match = Regex.Match(input, nonbacktrackingPattern)
                Console.WriteLine("   Pattern: {0}", nonbacktrackingPattern)
                If match.Success Then
                    Console.WriteLine("      Match: {0}", match.Value)
                    Console.WriteLine("      Group 1: {0}", match.Groups(1).Value)
                Else
                    Console.WriteLine("      Match failed.")
                End If
            Next
            Console.WriteLine()
        End Sub
    End Module
    ' The example displays the following output:
    '       Input: aaaaa
    '          Pattern: ((?>a+))\w
    '             Match failed.
    '       Input: aaaaab
    '          Pattern: ((?>a+))\w
    '             Match: aaaaab
    '             Group 1: aaaaa
    

    Aby uzyskać więcej informacji na temat grup atomowych, zobacz Konstrukcje grupowania.

  • Dopasowanie od prawej do lewej, które jest określane przez przekazanie opcji RegexOptions.RightToLeft do konstruktora klasy lub do statycznej metody dopasowywania instancji Regex. Ta funkcja jest przydatna podczas wyszukiwania od prawej do lewej zamiast od lewej do prawej lub w przypadkach, gdy jest bardziej wydajne, aby rozpocząć dopasowanie w prawej części wzorca zamiast po lewej stronie. Jak pokazano w poniższym przykładzie, użycie dopasowania od prawej do lewej może zmienić zachowanie chciwych kwantyfikatorów. W przykładzie są prowadzone dwa wyszukiwania w zdaniu kończącym się liczbą. Wyszukiwanie od lewej do prawej, które używa chciwego kwantyfikatora + , pasuje do jednej z sześciu cyfr w zdaniu, podczas gdy wyszukiwanie od prawej do lewej pasuje do wszystkich sześciu cyfr. Aby zapoznać się z opisem wzorca wyrażenia regularnego, zobacz przykład ilustrujący leniwe kwantyfikatory we wcześniejszej części tej sekcji.

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string greedyPattern = @".+(\d+)\.";
            string input = "This sentence ends with the number 107325.";
            Match match;
    
            // Match from left-to-right using lazy quantifier .+?.
            match = Regex.Match(input, greedyPattern);
            if (match.Success)
                Console.WriteLine($"Number at end of sentence (left-to-right): {match.Groups[1].Value}");
            else
                Console.WriteLine($"{greedyPattern} finds no match.");
    
            // Match from right-to-left using greedy quantifier .+.
            match = Regex.Match(input, greedyPattern, RegexOptions.RightToLeft);
            if (match.Success)
                Console.WriteLine($"Number at end of sentence (right-to-left): {match.Groups[1].Value}");
            else
                Console.WriteLine($"{greedyPattern} finds no match.");
        }
    }
    // The example displays the following output:
    //       Number at end of sentence (left-to-right): 5
    //       Number at end of sentence (right-to-left): 107325
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim greedyPattern As String = ".+(\d+)\."
            Dim input As String = "This sentence ends with the number 107325."
            Dim match As Match
    
            ' Match from left-to-right using lazy quantifier .+?.
            match = Regex.Match(input, greedyPattern)
            If match.Success Then
                Console.WriteLine("Number at end of sentence (left-to-right): {0}",
                                  match.Groups(1).Value)
            Else
                Console.WriteLine("{0} finds no match.", greedyPattern)
            End If
    
            ' Match from right-to-left using greedy quantifier .+.
            match = Regex.Match(input, greedyPattern, RegexOptions.RightToLeft)
            If match.Success Then
                Console.WriteLine("Number at end of sentence (right-to-left): {0}",
                                  match.Groups(1).Value)
            Else
                Console.WriteLine("{0} finds no match.", greedyPattern)
            End If
        End Sub
    End Module
    ' The example displays the following output:
    '       Number at end of sentence (left-to-right): 5
    '       Number at end of sentence (right-to-left): 107325
    

    Aby uzyskać więcej informacji na temat dopasowywania od prawej do lewej, zobacz Opcje wyrażeń regularnych.

  • Dodatnie i negatywne lookbehind: (?<=podwyrażenie) dla pozytywnego lookbehind i (?<!podwyrażenie) dla negatywnego lookbehind. Ta funkcja jest podobna do lookahead, która została omówiona wcześniej w tym temacie. Ponieważ aparat wyrażeń regularnych umożliwia pełne dopasowywanie od prawej do lewej, wyrażenia regularne umożliwiają nieograniczone wyszukiwanie. Pozytywne i negatywne spojrzenie wstecz można również stosować, aby uniknąć zagnieżdżania kwantyfikatorów, gdy zagnieżdżone podwyrażenie jest nadzbiorem wyrażenia zewnętrznego. Wyrażenia regularne z takimi zagnieżdżonymi kwantyfikatorami często mają niską wydajność. Na przykład poniższy przykład sprawdza, czy ciąg rozpoczyna się i kończy się znakiem alfanumerycznym, a każdy inny znak w ciągu jest jednym z większego podzestawu. Tworzy część wyrażenia regularnego używanego do weryfikowania adresów e-mail; Aby uzyskać więcej informacji, zobacz How to: Verify that Strings Are in Valid Email Format (Instrukcje: sprawdzanie, czy ciągi są w prawidłowym formacie poczty e-mail).

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
        public static void Main()
        {
            string[] inputs = { "jack.sprat", "dog#", "dog#1", "me.myself",
                              "me.myself!" };
            string pattern = @"^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$";
            foreach (string input in inputs)
            {
                if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
                    Console.WriteLine($"{input}: Valid");
                else
                    Console.WriteLine($"{input}: Invalid");
            }
        }
    }
    // The example displays the following output:
    //       jack.sprat: Valid
    //       dog#: Invalid
    //       dog#1: Valid
    //       me.myself: Valid
    //       me.myself!: Invalid
    
    Imports System.Text.RegularExpressions
    
    Module Example
        Public Sub Main()
            Dim inputs() As String = {"jack.sprat", "dog#", "dog#1", "me.myself",
                                       "me.myself!"}
            Dim pattern As String = "^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$"
            For Each input As String In inputs
                If Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase) Then
                    Console.WriteLine("{0}: Valid", input)
                Else
                    Console.WriteLine("{0}: Invalid", input)
                End If
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       jack.sprat: Valid
    '       dog#: Invalid
    '       dog#1: Valid
    '       me.myself: Valid
    '       me.myself!: Invalid
    

    Wyrażenie ^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$ regularne jest definiowane, jak pokazano w poniższej tabeli.

    Wzorzec opis
    ^ Rozpocznij dopasowanie na początku ciągu.
    [A-Z0-9] Dopasuj dowolny znak liczbowy lub alfanumeryczny. (Porównanie jest bez uwzględniania wielkości liter).
    ([-!#$%&'.*+/=?^`{}|~\w])* Dopasuj zero lub więcej wystąpień dowolnego znaku słowa lub dowolny z następujących znaków: -, !, #, $, %, &, ', ., *, +, /, =, ?, ^, `, {, }, | lub ~.
    (?<=[A-Z0-9]) Przyjrzyj się poprzedniemu znakowi, który musi być numeryczny lub alfanumeryczny. (Porównanie jest bez uwzględniania wielkości liter).
    $ Zakończ mecz na końcu ciągu.

    Aby uzyskać więcej informacji na temat pozytywnego i negatywnego lookbehind, zobacz Konstrukcje grupowania.

Nazwa opis
Przeszukiwanie wsteczne Zawiera informacje o tym, jak wyrażenia regularne obsługują wycofywanie i rozgałęzianie w celu znalezienia alternatywnych dopasowań.
Kompilacja i ponowne używanie Zawiera informacje o kompilowaniu i ponownym użyciu wyrażeń regularnych w celu zwiększenia wydajności.
Wyrażenia regularne .NET Przedstawia przegląd cech języka programowania w kontekście wyrażeń regularnych.
Model obiektów wyrażeń regularnych Zawiera informacje i przykłady kodu ilustrujące sposób używania klas wyrażeń regularnych.
Język wyrażeń regularnych — podręczny wykaz Zawiera informacje o zestawie znaków, operatorów i konstrukcji, których można użyć do definiowania wyrażeń regularnych.

Referencja