Ответ 1
Здесь более простой пример, чем тот, который приведен в документе @Dav:
string s0 = @"foo%123%456%789";
Regex r0 = new Regex(@"^([a-z]+)(?:%([0-9]+))+$");
Match m0 = r0.Match(s0);
if (m0.Success)
{
Console.WriteLine(@"full match: {0}", m0.Value);
Console.WriteLine(@"group #1: {0}", m0.Groups[1].Value);
Console.WriteLine(@"group #2: {0}", m0.Groups[2].Value);
Console.WriteLine(@"group #2 captures: {0}, {1}, {2}",
m0.Groups[2].Captures[0].Value,
m0.Groups[2].Captures[1].Value,
m0.Groups[2].Captures[2].Value);
}
результат:
full match: foo%123%456%789
group #1: foo
group #2: 789
group #2 captures: 123, 456, 789
Результаты full match
и group #1
просты, но другие требуют некоторого объяснения. Группа № 2, как вы можете видеть, находится внутри группы, не связанной с захватом, которая контролируется квантором +
. Он соответствует три раза, но если вы запросите его Value
, вы получите только то, что он сопоставил в третий раз - окончательный захват. Аналогичным образом, если вы используете заполнитель $2
в заменяющей строке, конечный захват - это то, что вставлено на его место.
В большинстве ароматов регулярных выражений это все, что вы можете получить; каждый промежуточный захват перезаписывается следующим и утерян;.NET почти уникален в сохранении всех захватов и делает их доступными после выполнения матча. Вы можете получить к ним доступ непосредственно, как я сделал здесь, или выполнить итерацию через CaptureCollection
, как и MatchCollection
. Однако нет эквивалента для заполнителей $1
-style replace-string.
Итак, причина, по которой дизайн API настолько уродлив (как вы выразились), двояка: сначала он был адаптирован из поддержки регулярного выражения Perl для объектно-ориентированной среды .NET; затем на него была привита структура CaptureCollection
. Perl 6 предлагает гораздо более чистое решение, но авторы сделали это, переписывая Perl практически с нуля и отбрасывая обратную совместимость с окном.