2015-12-02 2 views
4

Я работаю над C# Regex.Получить группу захвата всех итераций

Введите текст:

headera 
aa1aaa 
aa2aaa 
aa3aaa 

headerb 
aa4aaa 
aa5aaa 
aa6aaa 

headerc 
aa7aaa 
aa8aaa 
aa9aaa 

Я хотел бы, чтобы захватить число 4, 5 и 6 только, которые находятся между headerb и headerc

Мои попытки:

I смог захватить их под заголовками и заголовком с рисунком ниже. Я не могу применить ту же концепцию на lookbehind, так как это должно быть нулевой ширины, поэтому квантификаторы не допускаются.

aa(\d+)aaa(?=[\s|\S]*headerc) 

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

Пожалуйста, помогите. Thanks

[SOLVED] Использование преимуществ .Net, способного поддерживать внешний вид переменной ширины. Вы можете использовать шаблоны ниже:

@"(?<=headerb[\s|\S]*)aa(\d)aaa(?=[\s\S]*headerc)" 
@"(?s)(?<=\bheaderb\b.*?)\d+(?=.*?\bheaderc\b)" 
@"(?<=\bheaderb\b(?:(?!\bheaderc\b)[\s\S])*)aa(\d+)aaa" 
+2

Я бы рекомендовал сначала отфильтровать строки между 'headerb' и' headerc', а затем применить регулярное выражение к этим строкам выбора. –

+0

Я считаю, что вы можете использовать ['(?: Headerb | (?! ^) \ G) \ D * (\ d +) \ D * (? =. * Headerc)'] (http://regexstorm.net/tester ? р = (% 3f% 3aheaderb% 7c (% 3f!% 5e)% 5cG)% 5CD * (% 5CD% 2b)% 5CD * (% 3f% 3d. * headerc) & я = Headera% 0d% 0aaa1aaa% 0дн % 0aaa2aaa% 0d% 0aaa3aaa% 0D% 0A% 0D% 0aheaderb% 0d% 0aaa4aaa% 0d% 0aaa5aaa% 0d% 0aaa6aaa% 0D% 0A% 0D% 0aheaderc% 0d% 0aaa7aaa% 0d% 0aaa8aaa% 0D% 0aaa9aaa & O = s), но это может быть неэффективное решение. Аналогичный: ['(? S) (? <= \ Bheaderb \ b. *?) \ D + (? =. *? Headerc)'] (http://goo.gl/9NXp9h). Что касается * квантификаторов не допускается * - в .NET regex, lookbehinds являются переменной шириной. –

+1

Пожалуйста, смотрите [«Если вопросы включают« теги »в их названиях?»] (Http://meta.stackexchange.com/questions/19190/should-questions-include-tags-in-their-titles), где консенсус «нет, они не должны»! –

ответ

1

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

Во всяком случае, для этого конкретного примера что-то просто:

headerb\n(aa(\d+)aaa\n)+\nheaderc 

должен работать. Возможно, вам нужно будет отрегулировать пробелы/линии. Просто возьмите номер из группы (\ d +). P.S. вы cna используете названные группы для удобства, например. (?<number>\d+) создаст группу number.

Подробнее здесь: https://msdn.microsoft.com/en-us/library/bs2twtah(v=vs.110).aspx

4

C# поддерживает переменную lookbehind.So использовать его.

(?<=\bheaderb\b(?:(?!\bheaderc\b)[\s\S])*)aa(\d+)aaa 

See Demo.

+0

. Я не знал, что .Net поддерживает переменную lookbehind. –

+0

@DracoSahin теперь вы знаете :) – vks

+0

@DracoSahin действительно согласен, если это сработало для вас – vks

2

Вы регулярное выражение не соответствует тому, что вам нужно, потому что она не включает в себя границы. Примечание aa(\d+)aaa(?=[\s|\S]*headerc) соответствует aa, за которым следуют 1 или более цифр, за которыми следует любой символ ([\s\S] совпадает с [\s|\S]), 0 или более вхождений, а затем headerc. Таким образом, у вас нет ведущей границы.

Если вы настаиваете на регулярных выражений, вы можете использовать в переменной ширины в .NET 'назад регулярное выражение:

(?s)(?<=\bheaderb\b(?>(?!\bheader[bc]\b).)*)\d+ 

См demo.(?<=\bheaderb\b(?>(?!\bheader[bc]\b).)*) lookbehind гарантирует, что есть целое слово headerb или headerc и несколько 0 или более символов как можно меньше до последовательности цифр (обратите внимание на модификатор Singleline, который я добавил, чтобы заставить . соответствовать новой строке). (?>(?!\bheader[bc]\b).)* - это tempered greedy token, который соответствует любой подстроке, которая не содержит headerc или headerb целыми словами. Это необходимо в случае, если есть еще один блок headerb....headerc после headerc...headerd (см. Мою демонстрацию regex).

Однако решение регулярного выражения неэффективно (хотя потенциально «быстрое и грязное» одноразовое решение). Вы также можете использовать этот трюк: расколоть ввод с новой строкой символов в список «линии», найти блок вам нужно с помощью LINQ, а затем применить простое регулярное выражение, чтобы найти все последовательности цифр:

var lines = s.Split(new[] { "\r", "\n"}, StringSplitOptions.RemoveEmptyEntries); // Split into line array 
var subset = lines.SkipWhile(p => p != "headerb") // Get to the "headerb" line 
        .Skip(1) // Get to the line after "headerb" 
        .TakeWhile(m => m != "headerc") // Grab the lines in the block we need 
        .ToList(); 
var digits = Regex.Matches(string.Join(string.Empty, subset), "[0-9]+") 
       .Cast<Match>() 
       .Select(v => v.Value) 
       .ToList(); 

enter image description here

+0

. Это сработало. (? S) (? <= \ Bheaderb \ b. *?) \ D + (? =. *? \ Bheaderc \ b) Обратите внимание, что с помощью Regex.Matches() вы можете получить значение в match.Groups [0]. Поскольку мы смотрим вперед и оглядываемся назад, точное совпадение (4, 5, 6) мы только захватили. –

+0

Да, вы можете использовать его как 'var res = Regex.Matches (str, @" (? S) (? <= \ Bheaderb \ b. *?) \ D + (? =. *? \ Bheaderc \ b) ") .Cast () .Выберите (v => v.Value) .ToList(); '. Вы можете получить любые захваченные подстроки с помощью 'Match.Groups [n] .Value', где * n * - это число или имя группы захвата. В этом регулярном выражении нет групп захвата вообще, поэтому 'Match.Groups [0] .Value' можно просто заменить на' Match.Value' –

+0

@DracoSahin: Существуют .NET-совместимые онлайн-тестеры regex: [regexstorm. net] (http://regexstorm.net/tester) (я использовал его в своем ответе), [regexhero.net] (http://regexhero.net/tester), а некоторые другие менее известны. Можно использовать бесплатный инструмент, который также объясняет подшаблоны и генерирует .NET-код - [Expresso] (http://www.ultrapico.com/expresso.htm). –

1

Что об этом регулярное выражение:

(?<=headerb)[\r\n]*(?:aa(?<number>\d+)aaa[\r\n]*)+(?=headerc)

Code sample

Он печатает что вы хотите (4, 5, 6).

var regex = new Regex(@"(?<=headerb)[\r\n]*(?:aa(?<number>\d+)aaa[\r\n]*)+(?=headerc)", RegexOptions.Singleline); 
var match = regex.Match(<input>); 
if (match.Success) 
{ 
    foreach (var number in match.Groups["number"].Captures.Cast<Capture>()) 
    { 
     Console.WriteLine(number); 
    } 
} 
Смежные вопросы