Регулярное соответствие RegEx несколько раз в строке

Я пытаюсь извлечь значения из строки, которые находятся между < < и → . Но они могут произойти несколько раз.

Может ли кто-нибудь помочь с регулярным выражением, чтобы соответствовать этим,

this is a test for <<bob>> who like <<books>>
test 2 <<frank>> likes nothing
test 3 <<what>> <<on>> <<earth>> <<this>> <<is>> <<too>> <<much>>.

Затем я хочу, чтобы GroupCollection получил все значения.

Любая помощь очень получена. Спасибо.

Ответ 1

Используйте положительный взгляд вперед и посмотрите за утверждением, чтобы соответствовать угловым скобкам, используйте .*? чтобы найти максимально короткую последовательность символов между этими скобками. Найдите все значения, MatchCollection итерацию MatchCollection возвращенную методом Matches().

Regex regex = new Regex("(?<=<<).*?(?=>>)");

foreach (Match match in regex.Matches(
    "this is a test for <<bob>> who like <<books>>"))
{
    Console.WriteLine(match.Value);
}

LiveDemo в DotNetFiddle

Ответ 2

Вы можете попробовать один из них:

(?<=<<)[^>]+(?=>>)
(?<=<<)\w+(?=>>)

Однако вам придется перебирать возвращенный MatchCollection.

Ответ 4

Хотя ответ Питера является хорошим примером использования обходных путей для проверки контекста левой и правой стороны, я бы также хотел добавить LINQ (лямбда) способ доступа к матчам/группам и показать использование простых числовых групп захвата, которые пригодятся, когда вы хочу извлечь только часть шаблона:

using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;

// ...

var results = Regex.Matches(s, @"<<(.*?)>>", RegexOptions.Singleline)
            .Cast<Match>()
            .Select(x => x.Groups[1].Value);

Тот же подход с regex Питера:

var results = regex.Matches(s).Cast<Match>().Select(x => x.Value);

Примечание:

  • <<(.*?)>> является регулярным выражением, совпадающим с <<, затем фиксирует любые 0 или более символов как можно меньше (из-за не жадного квантификатора *?) в группу 1 и затем сопоставляет >>
  • RegexOptions.Singleline делает . соответствует символам новой строки (LF) (по умолчанию они не совпадают)
  • Cast<Match>() преобразует коллекцию совпадений в IEnumerable<Match> которому вы можете получить дальнейший доступ с помощью лямбды.
  • Select(x => x.Groups[1].Value) возвращает только значение группы 1 из текущего объекта совпадения x
  • Обратите внимание, что вы можете создать список массивов полученных значений, добавив .ToList() или .ToArray() после Select.

В демонстрационном коде С# string.Join(", ", results) генерирует разделенную запятыми строку значений группы 1:

var strs = new List<string> { "this is a test for <<bob>> who like <<books>>",
                              "test 2 <<frank>> likes nothing",
                              "test 3 <<what>> <<on>> <<earth>> <<this>> <<is>> <<too>> <<much>>." };
foreach (var s in strs) 
{
    var results = Regex.Matches(s, @"<<(.*?)>>", RegexOptions.Singleline)
            .Cast<Match>()
            .Select(x => x.Groups[1].Value);
    Console.WriteLine(string.Join(", ", results));
}

Выход:

bob, books
frank
what, on, earth, this, is, too, much