2013-07-07 3 views
0

я в настоящее время есть две отдельные регулярные выражения, чтобы найти целевое слово + следующего слова и целевое слово + предыдущее слово:Regex - Возвращение предыдущего и следующего слова из матча

string text = "Here is a test MYWORD statement for MYWORD regex"; 
string pattern = "(\\bMYWORD\\s)(\\w+)"; //MYWORD statement; MYWORD regex 
string pattern = "(\\w+)(\\s\\bMYWORD)"; //test MYWORD; for MYWORD 

ли обеспечить регулярное выражение изящный метод, чтобы объединить два шаблоны выше для использования с одним вызовом?

Благодаря

EDIT: Большое спасибо m.buettner и Qtax для больших объяснений и примеров - очень полезно!

Я попытался с некоторыми из приведенных примеров, и они соответствуют «MYWORD» в требуемом контексте, но, возможно, я не был достаточно ясен: я пытаюсь вернуть все фразы, указанные выше, например:

матчи (шаблон) должен вернуть все следующие строки:

'MYWORD statement' 
'MYWORD regex' 
'test MYWORD' 
'for MYWORD' 

Извинения, если мой первоначальный вопрос не объяснить, что достаточно хорошо!

+0

Что вы хотите совместить/найти/извлечь с помощью «единого вызова»? – Bohemian

+0

Обновленный вопрос для уточнения. – Vok

+0

@Vok, так значит, они должны * не совпадать, если это 'test MYWORD statement'? В противном случае мой ответ обрабатывает эти случаи. –

ответ

5

Совпадают внутри опережающего просмотра:

string pattern = @"\b(?=(\w+\s+MYWORD|MYWORD\s+\w+)\b)"; 

string[] result = Regex.Matches(text, pattern) 
         .Cast<Match>() 
         .Select(match => match.Groups[1].Value) 
         .ToArray(); 

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

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

string pattern = @"\b(?=((?:^|\w+\s+)MYWORD|MYWORD(?:\s+\w+|$))\b)"; 

UPDATE: Комментатор спросил, как захватить предшествующий и следующий слова без включения целевого слова. Ответ оказывается простым, но не очевидным:

string pattern = @"\b(?=((\w+)\s+MYWORD|MYWORD\s+(\w+))\b)"; 

string[] result = Regex.Matches(text, pattern) 
         .Cast<Match>() 
         .Select(match => match.Groups[2].Value + match.Groups[3].Value) 
         .ToArray(); 

Простая часть - это добавление групп захвата для отдельных слов. Неочевидная часть понимает, что в .NET, если группа захвата не участвует в матче, и вы получаете доступ к свойству Value, вы получаете пустую строку. Мы знаем, что в каждом матче будет участвовать только одна из двух групп. Нам не нужно знать, какой он был, мы просто хотим его ценность. Объединение значений строк дает нам именно то, что мы хотим.

Но он становится лучше:

string[] result = Regex.Matches(text, pattern) 
         .Cast<Match>() 
         .Select(match => match.Result("$2$3")) 
         .ToArray(); 

Result() метод не привыкают много, потому что остальная часть API .NET в Regex так хорошо разработан, но когда это полезно, это гениально!

+0

Выполняет именно то, что необходимо. Большое спасибо. – Vok

+0

Что делать, если я хочу захватить только предыдущие и конечные слова, а не поисковое слово MYWORD? Ex. заявление; регулярное выражение. Хотя это дает: инструкцию MYWORD; regex MYWORD; – Sanandrea

+0

@Sanandrea: Интересный вопрос! Я обновил ответ. –

0

Для примера что-то простое, как это будет работать:

(\w+)\sMYWORD\s(\w+) 

Но это требует, чтобы слова с обеих сторон MYWORD.

Если не может быть словом на какой стороне, вы могли бы сделать их необязательными, как:

(?:(\w+)\s)?\bMYWORD\b(?:\s(\w+))? 

Но что будет соответствовать MYWORD без слов вокруг него.

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

(?:(\w+)\sMYWORD\b(?:\s(\w+))?|\bMYWORD\s(\w+)) 

Altho здесь слово на правой пустошь либо в группе 2 или 3.

+1

Я не думаю, что .NET поддерживает '? |' –

+0

@ m.buettner, правильно, спасибо, что указали это. – Qtax

+0

Большое спасибо за ваши ответы и примеры. Я обновил исходный вопрос, чтобы уточнить мое намерение. – Vok

2

Прежде всего, некоторые советы: используйте стенографические строки. Они делают ускользает гораздо приятнее иметь дело с:

string pattern = @"(\bMYWORD\s)(\w+)"; //MYWORD statement; MYWORD regex 
string pattern = @"(\w+)(\s\bMYWORD)"; //test MYWORD; for MYWORD 

Обратите внимание, что ваша вторая модель имеет границу слова в неправильном конце:

string pattern = @"(\w+)(\sMYWORD\b)"; //test MYWORD; for MYWORD 

Теперь наивный подход просто:

string pattern = @"(\w+)\s(MYWORD)\s(\w+)"; 

У этого есть несколько проблем. Во-первых, это требует, чтобы оба слова были там, поэтому, если MYWORD появляется на одном конце строки, вы не получите никакого соответствия. Это можно устранить, если вместо слов указать якоря:

string pattern = @"(?:(\w+)\s|^)(MYWORD)(?:\s(\w+)|$)"; 

Теперь остается одна проблема. Матчи не могут пересекаться. Если у вас есть abc MYWORD def MYWORD ghi, второй MYWORD не будет соответствовать.Вы можете исправить это за счет исключения окружающих слова из матча, используя lookarounds:

string pattern = @"(?<=(\w+)\s|^)(MYWORD)(?=\s(\w+)|$)"; 

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

string pattern = @"(?<=(\w+)\s)?(MYWORD)(?=\s(\w+))?"; 

Working demo.

+0

Большое спасибо за ваши ответы и примеры. Я обновил исходный вопрос, чтобы уточнить мое намерение. – Vok

+0

@martin, как мы можем изменить это регулярное выражение, чтобы получить слова вокруг сопоставленного ключевого слова '(? <= (\ W +) \ s)? (Продолжить) (? = \ S (\ w +))?' – Learning

+1

@Learning быть в состоянии получить их, не изменяя регулярное выражение вообще. Проверьте 'match.Groups [1] .Value' и' match.Groups [3] .Value' (вы можете использовать их свойства «Успех», чтобы определить, присутствовало ли это слово или нет). –