Странное поведение регулярного выражения — соответствует только первой и последней группе захвата

Я пытаюсь написать регулярное выражение, которое будет соответствовать списку слов, разделенных запятыми, и захватить все слова. Эта строка должна соответствовать    apple , banana ,orange,peanut , а захваты должны быть apple, banana, orange, peanut. Для этого я использую следующее регулярное выражение:

^\s*([a-z_]\w*)(?:\s*,\s*([a-z_]\w*))*\s*$

Он успешно соответствует строке, но внезапно захватываются только apple и peanut. Такое поведение наблюдается как в C#, так и в Perl. Таким образом, я предполагаю, что мне что-то не хватает в том, как работает сопоставление регулярных выражений. Есть идеи? :)


person bazzilic    schedule 19.11.2012    source источник


Ответы (4)


Значение, заданное match.Groups[2].Value, является просто последним значением, захваченным второй группой.

Чтобы найти все значения, посмотрите на match.Groups[2].Captures[i].Value, где в данном случае i находится в диапазоне от 0 до 2. (А также match.Groups[1].Value для первой группы.)

(+1 за вопрос, сегодня я кое-что узнал!)

person Rawling    schedule 19.11.2012
comment
+1 Я не был уверен, что в .net есть такая функция, и было лень проверять. - person stema; 19.11.2012
comment
@stema Я даже не осознавал, что это проблема, я думал, что все значения окажутся в Groups! - person Rawling; 19.11.2012
comment
Спасибо, что указали мне на .Captures коллекцию! В конце концов я придумал использовать то же регулярное выражение, что и в вопросе, а затем я делаю for (int i = 1; i < match.Groups.Count; i++) foreach (var capture in match.Groups[i].Captures) { ... do smth with capture ... } - person bazzilic; 19.11.2012

Попробуй это:

string text = "   apple , banana ,orange,peanut";

var matches = Regex.Matches(text, @"\s*(?<word>\w+)\s*,?")
        .Cast<Match>()
        .Select(x => x.Groups["word"].Value)
        .ToList();
person Rui Jarimba    schedule 19.11.2012
comment
PS: Это хороший сайт для тестирования регулярных выражений в .NET: Regex Hero - person Rui Jarimba; 19.11.2012
comment
Мне нравится этот. - person Chankey Pathak; 19.11.2012
comment
Спасибо @bazzilic и ChankeyPathak, я не знал этих сайтов. - person Rui Jarimba; 21.11.2012

Вы повторяете свою группу захвата, при каждом повторном совпадении предыдущий контент перезаписывается. Таким образом, в конце будет доступно только последнее совпадение вашей второй группы захвата.

Вы можете изменить вторую группу захвата на

^\s*([a-z_]\w*)((?:\s*,\s*(?:[a-z_]\w*))*)\s*$

Тогда результатом будет «банан, апельсин, арахис» во второй группе. Я не уверен, хотите ли вы этого.

Если вы хотите проверить, что строка имеет этот шаблон, и извлечь каждое слово. Я бы сделал это в два этапа.

  1. Проверьте шаблон с вашим регулярным выражением.

  2. Если шаблон правильный, удалите начальные и конечные пробелы и разделите на \s*,\s*.

person stema    schedule 19.11.2012

Простое регулярное выражение:

(?:^| *)(.+?)(?:,|$)

Объяснение:

?:    # Non capturing group
^| *  # Match start of line or multiple spaces
.+    # Capture the word in the list, lazy
?:    # Non capture group
,|$   # Match comma or end of line 

Примечание. Rubular — отличный сайт для тестирования подобных вещей.

person Chris Seymour    schedule 19.11.2012