В регулярном выражении С# почему первичное совпадение отображается в группах?

Итак, если я напишу регулярное выражение, оно будет соответствовать, я могу получить совпадение, или я могу получить доступ к его группам. Это кажется встречным интуитивным, поскольку группы определены в выражении с фигурными скобками "(" и ")". Похоже, что это не только неправильно, но и избыточно. Кто-нибудь знает почему?

Regex quickCheck = new Regex(@"(\D+)\d+");
string source = "abc123";

m.Value        //Equals source
m.Groups.Count //Equals 2
m.Groups[0])   //Equals source
m.Groups[1])   //Equals "abc"

Ответ 1

Я согласен - это немного странно, но я думаю, что для этого есть веские причины.

Regex Match само по себе является Group, которое, в свою очередь, является Capture.

Но Match.Value (или Capture.Value как он есть на самом деле) действителен только тогда, когда в строке присутствует одно совпадение - если вы сопоставляете несколько экземпляров шаблона, то по определению он не может вернуть все, В действительности - свойство Value в матче - это удобство, когда есть только совпадение.

Но для того, чтобы выяснить, как это поведение передачи целого совпадения в Groups[0] имеет смысл - рассмотрим этот (надуманный) пример наивного кода unminifier:

[TestMethod]
public void UnMinifyExample()
{
  string toUnMinify = "{int somevalue = 0; /*init the value*/} /* end */";
  string result = Regex.Replace(toUnMinify, @"(;|})\s*(/\*[^*]*?\*/)?\s*", "$0\n");
  Assert.AreEqual("{int somevalue = 0; /*init the value*/\n} /* end */\n", result);
}

Соответствие регулярному выражению сохранит /* */comments в конце инструкции, после чего добавит новую строку - но работает для; или} строк.

Хорошо - вы могли бы задаться вопросом, почему вы это делаете с помощью регулярного выражения, но меня юмора:)

Если Groups[0], сгенерированное совпадением для этого регулярного выражения, не было целым захватом - тогда замена одного вызова была бы невозможна - и ваш вопрос, вероятно, будет задавать вопрос, почему не весь матч попадет в Groups[0] вместо другого пути!

Ответ 2

Документация для Match говорит о том, что первая группа всегда является полным совпадением, поэтому она не является детализацией реализации.

Ответ 3

Это историческое все. В Perl 5 содержимое групп захвата хранится в специальных переменных $1, $2 и т.д., Но С#, Java и другие вместо этого сохраняют их в массиве (или в виде массива). Чтобы сохранить совместимость с соглашением об именах Perl (которое было скопировано несколькими другими языками), первая группа хранится в элементе номер один, второй в элементе два и т.д. Это оставляет элемент без нуля, поэтому почему бы не сохранить полное соответствие там?

FYI, Perl 6 принял новое соглашение, в котором первая группа захвата нумерует нуль вместо единицы. Я уверен, что это было сделано не для того, чтобы нас разозлить.;)

Ответ 4

Не знаю, почему, но если вы используете именованные группы, вы можете установить опцию RegExOptions.ExplicitCapture, и она не должна включать источник как первая группа.

Ответ 5

Скорее всего, вы можете использовать "$ 0" для представления соответствия в выражении подстановки и "$ 1" для первого группового совпадения и т.д.

Ответ 6

Я не думаю, что на самом деле ответ, кроме человека, который написал это, выбрал это как деталь реализации. Пока вы помните, что первая группа всегда будет равна исходной строке, вы должны быть в порядке: -)

Ответ 7

Он может быть избыточным, однако он обладает некоторыми хорошими свойствами.

Например, это означает, что группы захвата работают так же, как и другие двигатели регулярных выражений - первая группа захвата соответствует "1" и т.д.

Ответ 8

Backreferences являются однонаправленными, например, \1 или $1 являются первым подвыражением в скобках и т.д. Как сказано выше, один из них сопоставляется с другим без всяких мыслей.

Также обратите внимание: m.Groups["0"] дает вам всю согласованную подстроку, поэтому обязательно пропустите "0", если вы повторяете regex.GetGroupNames().