Проблема в вашем примере состоит в том, что последнее слово вашей первой цели - это первое слово вашей второй цели в исходной строке; «нормальный» синтаксис RegEx заставляет механизм RE потреблять совпадающие символы, т. е. эти символы недоступны для дальнейших совпадений.
Вы можете сделать это - в принципе - с помощью разновидностей Regex, таких как PCRE, используя группы захвата в lookahead утверждения, так как они не приводят к потреблению персонажа в утверждении. Но все это утверждение имеет свою цену в производительности. Матчи пройдут в двух группах захвата. Два примера:
Прямой подход:
/
\b(?=(.*?cheese))butter # match butter, assert that cheese comes after it and capture
| # or
\b(?=(.*?butter))cheese # match cheese, assert that butter comes after it and capture
/gsx # flags: global, single line, free spacing
Давайте посмотрим, как работает успешное совпадение \b(?=(.*?cheese))butter
; тот же принцип отражен в другой альтернативе. Механизм регулярных выражений сначала ищет границу слова \b
, то есть позицию в тексте, которая не имеет символа слова с обеих сторон. Найдя его, он попытается установить (?=(.*?cheese))
в этой позиции. На естественном языке: «Начиная здесь, найдите cheese
как можно скорее. Только если вы найдете его, запишите всю строку, которую вы только что прошли в пронумерованной группе, и верните указатель совпадения туда, где мы начали. Затем разрешите сопоставление, чтобы продолжить «. Если утверждение было успешным, сопоставление продолжается и затем используется butter
. У нас есть совпадение, указатель совпадения стоит за butter
, и механизм регулярных выражений пытается выполнить то же самое (включая, конечно, альтернативу) в остальной части текста.
См. regex demo .
Несколько оптимизированная версия:
/
\b(?=((?:[^c]*+|c(?!heese))*cheese))butter
|
\b(?=((?:[^b]*+|b(?!utter))*butter))cheese
/gsx
См. regex demo .
Вывод:
Match 1
Full match 27-33 butter
Group 1. 27-70 butter is called "pindakaas" (peanut cheese
Match 2
Full match 64-70 cheese
Group 2. 64-111 cheese) rather than "pindaboter" (peanut butter
ИЛИ
Если каждый не против объединения совпадающей строки и захваченной строки для каждого совпадения после факта, это также будет работать и будет лучше с точки зрения производительности. (Вероятно, все еще не так хорошо, как решение overlap
, увиденное в ответе Booboo.)
/\bbutter\b.*?\b(?=(cheese))|\bcheese\b.*?\b(?=(butter))/sg
Это соответствует каждому варианту только до границы слова перед вторым термином, что позволяет начать следующую попытку сопоставления с этим термином. Второй член не является частью строки соответствия, но хранится в захваченной группе: ['butter is called "pindakaas" (peanut ', 'cheese'], etc.
.
См. regex demo .