кванторы в регулярных выражениях
Квантификаторы определяют количество экземпляров символа, группы или класса символов, которое должно присутствовать во входных данных, чтобы было зафиксировано совпадение. В следующей таблице перечислены квантификаторы, поддерживаемые .NET:
Жадный квантификатор | Ленивый квантификатор | Описание |
---|---|---|
* |
*? |
Соответствует нулю или более раз. |
+ |
+? |
Совпадает один или несколько раз. |
? |
?? |
Соответствует нулю или одному разу. |
{
n} |
{
n}? |
Соответствует ровно n раз. |
{
n,} |
{
n,}? |
Соответствует по крайней мере n раз. |
{
n, m} |
{
n, m}? |
Совпадения от n до m раз. |
Количества n
и m
являются целочисленными константами. Как правило, квантификаторы жадны. Они заставляют механизм регулярных выражений пытаться сопоставить как можно больше вхождений конкретных шаблонов. Добавление символа к ?
квантификатору делает его ленивым. Это приводит к тому, что механизм регулярных выражений будет соответствовать настолько малому количеству вхождений, насколько это возможно. Полное описание разницы между жадными и ленивыми квантификаторами см. в разделе Greedy и Lazy Quantifiers далее в этой статье.
Внимание
Вложенные квантификаторы, такие как шаблон регулярного выражения (a*)*
, могут увеличить количество сравнений, которые должен выполнять движок регулярных выражений. Число сравнений может увеличиваться в виде экспоненциальной функции числа символов во входной строке. Дополнительные сведения о данном поведении и обходных путях см. в разделе поиск с возвратом.
Квантификаторы регулярных выражений
В следующих разделах перечислены квантификаторы, поддерживаемые регулярными выражениями .NET:
Примечание.
Если в шаблоне регулярных выражений встречаются символы *, +, ?, { или }, обработчик регулярных выражений интерпретирует их как квантификаторы или как часть конструкций квантификаторов, если они не включены в класс символов. Чтобы они интерпретировались как символы-литералы за пределами класса символов, необходимо ставить перед ними escape-символ — обратную косую черту. Например, строка \*
в шаблоне регулярного выражения интерпретируется как литеральный символ звездочки ("*").
Совпадение нуль или более раз: *
Квантификатор *
соответствует предыдущему элементу ноль или более раз. Это эквивалентно квантификатору {0,}
.
*
— жадный квантификатор, ленивым эквивалентом которого является квантификатор *?
.
В следующем примере показано, как использовать это регулярное выражение. Пять из девяти цифр-групп в входной строке соответствуют шаблону и четыре (95
, 929
9219
и9919
) не соответствуют.
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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Указывает, что совпадение должно начинаться на границе слова. |
91* |
Совпадает с 9 , за которым следует ноль или более символов 1 . |
9* |
Соответствует нулю или нескольким 9 символам. |
\b |
Указывает, что совпадение должно заканчиваться на границе слова. |
Совпадение один или несколько раз: +
Квантификатор +
сопоставляет предыдущий элемент один или несколько раз. Это эквивалентно {1,}
.
+
— жадный квантификатор, ленивым эквивалентом которого является квантификатор +?
.
Например, с помощью регулярного выражения \ban+\w*?\b
осуществляется сопоставление целых слов, начинающихся с буквы a
, за которой следует одна или несколько букв n
. В следующем примере показано, как использовать это регулярное выражение. Регулярное выражение соответствует словам an
, annual
, announcement
и antique
и не соответствует словам autumn
и 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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните на границе слова. |
an+ |
Совпадает с a , за которым следует один или несколько символов n . |
\w*? |
Соответствует символу слова ноль или больше раз, но как можно меньше. |
\b |
Конец на границе слова. |
Совпадение ноль или один раз: ?
Квантификатор ?
сопоставляет предыдущий элемент ноль или один раз. Это эквивалентно {0,1}
.
?
— жадный квантификатор, ленивым эквивалентом которого является квантификатор ??
.
Например, регулярное выражение \ban?\b
пытается сопоставить все слова, начинающиеся с буквы a
, за которой следует ноль или один экземпляр буквы n
. Иными словами, предпринимается попытка найти слова a
и an
. В следующем примере показано следующее регулярное выражение:
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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните с границы слова. |
an? |
Совпадает с a , за которым следует ноль или один символ n . |
\b |
Завершите на границе слова. |
Совпадение ровно n раз: {n}
Квантификатор {
n}
сопоставляет предыдущий элемент ровно n раз, где n — любое целое число.
{
n}
— жадный квантификатор, ленивым эквивалентом которого является квантификатор {
n}?
.
Например, регулярное выражение \b\d+\,\d{3}\b
ищет границу слова, за которой следует один или более десятичных знаков, затем три десятичных знака и снова граница слова. В следующем примере показано следующее регулярное выражение:
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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните с границы слова. |
\d+ |
Соответствует одному или нескольким десятичным цифрам. |
\, |
Соответствует символу запятой. |
\d{3} |
Соответствует трем десятичным цифрам. |
\b |
Закончите на границе слова. |
Совпадение как минимум n раз: {n,}
Квантификатор {
n,}
сопоставляет предыдущий элемент как минимум n раз, где n — любое целое число.
{
n,}
— жадный квантификатор, ленивым эквивалентом которого является квантификатор {
n,}?
.
Например, регулярное выражение \b\d{2,}\b\D+
пытается найти границу слова, за которой следуют по крайней мере две цифры, граница слова и символ, не являющийся цифрой. В следующем примере показано, как использовать это регулярное выражение. Регулярное выражение не соответствует фразе "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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните с границы слова. |
\d{2,} |
Соответствует не менее чем двум десятичным цифрам. |
\b |
Соответствует границе слова. |
\D+ |
Соответствует хотя бы одной не десятичной цифре. |
Сопоставление от n до m раз: {n,m}
Квантификатор {
n,
m}
сопоставляет предыдущий элемент минимум n раз, но не больше m раз, где n и m — целые числа.
{
n,
m}
— жадный квантификатор, ленивым эквивалентом которого является квантификатор {
n,
m}?
.
В следующем примере с помощью регулярного выражения (00\s){2,4}
осуществляется поиск от двух до четырех вхождений двух нулей, за которыми следует пробел. Последняя часть входной строки включает этот шаблон пять раз, а не максимум 4. Однако только начало этой части строки (до пробела и пятой пары нулей) соответствует шаблону регулярного выражения.
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($"'{match.Value}' found at position {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.
Совпадение ноль или несколько раз (ленивое совпадение): *?
Квантификатор *?
соответствует предыдущему элементу ноль или более раз, но при этом как можно меньше раз. Это ленивый аналог жадного квантификатора *
.
В следующем примере регулярное выражение \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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните на границе слова. |
\w*? |
Соответствует нулю или нескольким символам слова, но как можно меньше символов. |
oo |
Соответствует строке oo . |
\w*? |
Соответствует нулю или нескольким символам слова, но как можно меньше символов. |
\b |
Конец на границе слова. |
Совпадение один или несколько раз (ленивое совпадение): +?
Квантификатор +?
соответствует предыдущему элементу один или несколько раз, но как можно меньше. Это ленивый аналог жадного квантификатора +
.
Например, регулярное выражение \b\w+?\b
соответствует одному или нескольким символам, разделенным границами слов. В следующем примере показано следующее регулярное выражение:
string pattern = @"\b\w+?\b";
string input = "Aa Bb Cc Dd Ee Ff";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine($"'{match.Value}' found at position {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.
Совпадение ноль или один раз (ленивое совпадение): ??
Квантификатор ??
соответствует предыдущему элементу ноль или один раз, но как можно меньше. Это ленивый аналог жадного квантификатора ?
.
Например, регулярное выражение ^\s*(System.)??Console.Write(Line)??\(??
пытается сопоставить строки Console.Write
или Console.WriteLine
. Строка также может включать System.
перед Console
, и за ней может следовать открывающая скобка. Строка должна находиться в начале строки, хотя перед ней могут стоять пробелы. В следующем примере показано следующее регулярное выражение:
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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
^ |
Соответствует началу входного потока. |
\s* |
Соответствует нулю или нескольким символам пробела. |
(System.)?? |
Соответствует нулю или одному совпадению строки System. . |
Console.Write |
Соответствует строке Console.Write . |
(Line)?? |
Соответствует нулю или одному вхождению строки Line . |
\(?? |
Соответствует нулю или одному вхождению открывающей скобки. |
Совпадение ровно n раз (ленивое совпадение): {n}?
Квантификатор {
n}?
сопоставляет предыдущий элемент ровно n
раз, где n — любое целое число. Это ленивый аналог жадного квантификатора {
n}
.
В следующем примере регулярное выражение \b(\w{3,}?\.){2}?\w{3,}?\b
используется для идентификации адреса веб-сайта. Выражение совпадает с 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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начните с границы слова. |
(\w{3,}?\.) |
Соответствует по крайней мере три символа слова, но как можно меньше символов, за которым следует точка или символ периода. Этот шаблон является первой группой записи. |
(\w{3,}?\.){2}? |
Соответствует шаблону в первой группе два раза, по возможности меньше раз. |
\b |
Завершите сопоставление на границе слова. |
Совпадение как минимум n раз (ленивое совпадение): {n,}?
Квантификатор {
n,}?
соответствует предыдущему элементу по крайней мере n
раз, где n является любым целым числом, но как можно меньше. Это ленивый аналог жадного квантификатора {
n,}
.
См. пример для квантификатора {
n}?
в предыдущем разделе для иллюстрации. В регулярном выражении из этого примера квантификатор {
n,}
используется для поиска строки, состоящей по крайней мере из трех символов, после которых стоит точка.
Совпадение от n до m раз (ленивое совпадение): {n,m}?
Квантификатор {
n,
m}?
соответствует предыдущему элементу от n
до m
раз, где n и m являются целыми числами, но как можно меньше раз. Это ленивый аналог жадного квантификатора {
n,
m}
.
В следующем примере регулярное выражение \b[A-Z](\w*?\s*?){1,10}[.!?]
соответствует предложениям, содержащим от 1 до 10 слов. Ему соответствуют все предложения в исходной строке кроме одного, длина которого составляет 18 слов.
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($"'{match.Value}' found at position {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.
Шаблон регулярного выражения определен, как показано в следующей таблице:
Расписание | Описание |
---|---|
\b |
Начинайте с границы слова. |
[A-Z] |
Соответствует верхнему регистру от A до Z. |
(\w*?\s*?) |
Соответствует нулю или нескольким символам слова, за которым следует один или несколько символов пробелов, но как можно меньше. Этот шаблон является первой группой записи. |
{1,10} |
Соответствует предыдущему шаблону от 1 до 10 раз. |
[.!?] |
Соответствует любому из символов препинания . , ! или ? . |
Жадные и ленивые квантификаторы
Некоторые квантификаторы имеют две версии:
Жадная версия.
Жадный квантификатор пытается найти максимально возможное число соответствий элемента.
Нежадный (ленивый) вариант.
При использовании нежадных идентификаторов предпринимается попытка найти минимально возможное число соответствий элемента. Вы можете превратить жадный квантификатор в ленивый квантификатор, добавив a
?
.
Рассмотрим регулярное выражение, которое предназначено для извлечения последних четырех цифр из строки чисел, таких как номер кредитной карты. Версия регулярного выражения с жадным квантификатором *
будет выглядеть так: \b.*([0-9]{4})\b
. Однако если строка содержит два числа, это регулярное выражение соответствует последним четырем цифрам только второго числа, как показано в следующем примере:
string greedyPattern = @"\b.*([0-9]{4})\b";
string input1 = "1112223333 3992991999";
foreach (Match match in Regex.Matches(input1, greedyPattern))
Console.WriteLine($"Account ending in ******{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 ******{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}
и их ленивые аналоги никогда не повторяются после пустого соответствия, если найдено минимальное количество совпадений. Это правило препятствует вхождению квантификаторов в бесконечные циклы при пустых соответствиях частей выражений, если максимальное количество возможных фиксаций группы бесконечно или приближено к бесконечному.
Например, в следующем коде показан результат вызова метода Regex.Match с шаблоном регулярного выражения (a?)*
, который соответствует нулю или одному a
символу ноль или несколько раз. Одна группа захвата захватывает каждую a
и String.Empty, но нет второго пустого совпадения, так как первое пустое совпадение приводит квантификатор к прекращению повторения.
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: '{match.Value}' at index {match.Index}");
if (match.Groups.Count > 1) {
GroupCollection groups = match.Groups;
for (int grpCtr = 1; grpCtr <= groups.Count - 1; grpCtr++) {
Console.WriteLine($" Group {grpCtr}: '{groups[grpCtr].Value}' at index {groups[grpCtr].Index}");
int captureCtr = 0;
foreach (Capture capture in groups[grpCtr].Captures) {
captureCtr++;
Console.WriteLine($" Capture {captureCtr}: '{capture.Value}' at index {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}
. Оба регулярных выражения состоят из одной группы записи, которая определена в следующей таблице:
Расписание | Описание |
---|---|
(a\1 |
Либо совпадает a со значением первой захваченной группы ... |
|(?(1) |
… или проверяет, определена ли первая захваченная группа. Конструкция (?(1) не определяет группу записи. |
\1)) |
Если первая захваченная группа существует, следует сопоставить ее значение. Если группа не существует, она будет соответствовать String.Empty. |
Первое регулярное выражение пытается сопоставить этот шаблон от нуля до двух раз. Второй — ровно два раза. Так как первый шаблон достигает минимального количества захватов с его первым захватом String.Empty, он никогда не повторяется, чтобы попытаться сопоставить a\1
. Квантификатор {0,2}
разрешает только пустые совпадения в последней итерации. В отличие от этого, второе регулярное выражение совпадает с a
, так как во второй раз выполняется a\1
. Минимальное количество итераций, 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: {pattern}");
Match match = Regex.Match(input, pattern);
Console.WriteLine($"Match: '{match.Value}' at position {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: {groupCtr}: '{group.Value}' at position {group.Index}.");
int captureCtr = 0;
foreach (Capture capture in group.Captures) {
captureCtr++;
Console.WriteLine($" Capture: {captureCtr}: '{capture.Value}' at position {capture.Index}.");
}
}
}
Console.WriteLine();
pattern = @"(a\1|(?(1)\1)){2}";
Console.WriteLine($"Regex pattern: {pattern}");
match = Regex.Match(input, pattern);
Console.WriteLine($"Matched '{match.Value}' at position {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: {groupCtr}: '{group.Value}' at position {group.Index}.");
int captureCtr = 0;
foreach (Capture capture in group.Captures) {
captureCtr++;
Console.WriteLine($" Capture: {captureCtr}: '{capture.Value}' at position {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.