В регулярном выражении С# почему первичное совпадение отображается в группах?
Итак, если я напишу регулярное выражение, оно будет соответствовать, я могу получить совпадение, или я могу получить доступ к его группам. Это кажется встречным интуитивным, поскольку группы определены в выражении с фигурными скобками "(" и ")". Похоже, что это не только неправильно, но и избыточно. Кто-нибудь знает почему?
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()
.