Regex - получает строку между двумя словами, которая не содержит слова - PullRequest
6 голосов
/ 07 сентября 2011

Я искал вокруг и не мог заставить это случиться.Я не совсем noob.

Мне нужно получить текст, разделенный (включая) START и END, который не содержит START.По сути, я не могу найти способ отрицать целое слово без использования сложного материала.

Пример строки:

abcSTARTabcSTARTabcENDabc

Ожидаемый результат:

STARTabcEND

Не хорошо:

STARTabcSTARTabcEND

Я не могу использовать материалы для обратного поиска.Я проверяю свое регулярное выражение здесь: www.regextester.com

Спасибо за любой совет.

Ответы [ 5 ]

10 голосов
/ 07 сентября 2011

Попробуй

START(?!.*START).*?END

Посмотри здесь, на Regexr

(?!.*START) - это негативная перспектива.Это гарантирует, что слово «START» не следует за

.*? - это не жадное совпадение всех символов до следующего «END».Это необходимо, потому что отрицательный прогноз просто смотрит вперед и ничего не захватывает (утверждение нулевой длины)

Обновление:

Я подумал немного больше, решение выше совпадает до первого "КОНЕЦ"».Если это нежелательно (поскольку вы исключаете START из содержимого), тогда используйте жадную версию

START(?!.*START).*END

, которая будет соответствовать до последнего "END".

4 голосов
/ 05 октября 2011
START(?:(?!START).)*END

будет работать с любым количеством пар START...END.Чтобы продемонстрировать на Python:

>>> import re
>>> a = "abcSTARTdefENDghiSTARTjlkENDopqSTARTrstSTARTuvwENDxyz"
>>> re.findall(r"START(?:(?!START).)*END", a)
['STARTdefEND', 'STARTjlkEND', 'STARTuvwEND']

Если вы заботитесь только о контенте от START до END, используйте это:

(?<=START)(?:(?!START).)*(?=END)

Смотрите здесь:

>>> re.findall(r"(?<=START)(?:(?!START).)*(?=END)", a)
['def', 'jlk', 'uvw']
4 голосов
/ 07 сентября 2011

По-настоящему пешеходным решением будет START(([^S]|S*S[^ST]|ST[^A]|STA[^R]|STAR[^T])*(S(T(AR?)?)?)?)END. У современных разновидностей регулярных выражений есть отрицательные утверждения, которые делают это более элегантно, но я интерпретирую ваш комментарий о «обратном поиске», чтобы, возможно, означать, что вы не можете или не хотите использовать эту функцию.

Обновление : только для полноты обратите внимание, что приведенное выше является жадным по отношению к конечному разделителю. Чтобы захватить только самую короткую возможную строку, расширьте отрицание, чтобы охватить также конечный разделитель - START(([^ES]|E*E[^ENS]|EN[^DS]|S*S[^STE]|ST[^AE]|STA[^RE]|STAR[^TE])*(S(T(AR?)?)?|EN?)?)END. Однако в большинстве культур это может превысить порог пыток.

Исправление ошибки: Предыдущая версия этого ответа содержала ошибку, в которой SSTART мог быть частью совпадения (второй S соответствовал бы [^T] и т. Д.). Я исправил это, но добавив S в [^ST] и добавив S* перед необязательным S, чтобы учесть произвольные повторения S в противном случае.

2 голосов
/ 04 июня 2014

Могу ли я предложить возможное улучшение решения Тима Пицкера?Мне кажется, что START(?:(?!START).)*?END лучше, чтобы поймать только START, за которым сразу следует END без каких-либо START или END между ними.Я использую .NET, и решение Тима также будет соответствовать примерно START END END.По крайней мере, в моем личном случае это не нужно.

0 голосов
/ 07 сентября 2011

[РЕДАКТИРОВАТЬ: я оставил этот пост для информации о группах захвата, но основное решение, которое я дал, не было правильным.(?:START)((?:[^S]|S[^T]|ST[^A]|STA[^R]|STAR[^T])*)(?:END), как указано в комментариях, не будет работать;Я забыл, что игнорируемые символы не могут быть отброшены, и поэтому вам нужно что-то такое, как ... |STA(?![^R])|, чтобы по-прежнему разрешать этому символу быть частью END, таким образом сбой чего-то такого, как STARTSTAEND;так что это явно лучший выбор;следующее должно показать правильный способ использования групп захвата ...]

Ответ, полученный с помощью оператора '?!' с нулевой шириной, с группами захвата: (?:START)((?!.*START).*)(?:END)захватывает внутренний текст, используя $ 1 для замены.Если вы хотите, чтобы теги START и END были захвачены, вы можете сделать (START)((?!.*START).*)(END), что дает $ 1 = START $ 2 = текст и $ 3 = END или другие различные перестановки, добавляя / удаляя () s или ?: s.

Таким образом, если вы используете его для поиска и замены, вы можете сделать что-то вроде BEGIN $ 1FINISH.Итак, если вы начали с:

abcSTARTdefSTARTghiENDjkl

, вы получите ghi в качестве группы захвата 1, а замена на BEGIN $ 1FINISH даст вам следующее:

abcSTARTdefBEGINghiFINISHjkl

, что позволит вам менять токены START / END только при правильном сопряжении.

Каждый (x) является группой, но я поставил (?:x) для каждого из них, кроме середины, которая помечает его как группу без захвата;единственным, кого я оставил без ?:, была середина;тем не менее, вы также можете получить токены BEGIN / END, если хотите переместить их или что-то еще.

См. документацию Java regex для получения полной информации о Javaрегулярные выражения.

...