Я хотел бы использовать регулярное выражение для замены слова ABC между двумя словами MNO и XYZ на '', но не вхождения слова ABC, которые не находятся между MNO и XYZ.
Например, учитывая следующую строку:
Lorem ABC ipsum ABC bla MNO bla ipsum ABC asfg 123 hello ABC dd ABC XYZ hello ABC
Ожидаемый результат будет следующим:
Lorem ABC ipsum ABC bla MNO bla ipsum asfg 123 hello dd XYZ hello ABC
Таким образом, заменены только ABC три между MNO и XYZ.
Я попробовал несколько регулярных выражений внутри preg_replace в PHP, но безуспешно.
Например, в этом я не знаю, как не сопоставить все, кроме ABC:
/(?<=MNO)(.*)ABC(.*)(?=XYZ)/g
Я был бы признателен за использование регулярных выражений и preg_replace в этом случае.
Любые идеи? Спасибо
Виктор, к preg_replace_callback можно привыкнуть.
Могут ли MNO и XYZ встречаться более одного раза?
Виктор, добавил к вопросу один пример, который может пролить свет на него.
Редко, в данном случае только один раз, но я не против






Это сработает, если в предложении есть только одно вхождение MNO и XYZ.
Попробуйте это регулярное выражение:
ABC(?!.*MNO)(?=.*XYZ)
Замените каждое совпадение пустой строкой
Объяснение:
ABC - соответствует ABC(?!.*MNO) - отрицательный просмотр вперед, чтобы убедиться, что за текущим совпадением не следует MNO где-то позже в строке(?=.*XYZ) - положительный просмотр вперед, чтобы убедиться, что за текущим совпадением следует XYZ где-то позже в строкеКод(РЕЗУЛЬТАТ)
$re = '/ABC(?!.*MNO)(?=.*XYZ)/m';
$str = 'Lorem `ABC` ipsum ABC bla MNO bla ipsum ABC asfg 123 hello ABC dd ABC XYZ hello ABC
';
$subst = '';
$result = preg_replace($re, $subst, $str);
echo "The result of the substitution is ".$result;
Работает идеально, это как раз то, что мне нужно.
megatxi Я чувствую, что решение, предоставленное @WiktorStribiżew, намного лучше и должно быть принято в качестве решения.
Вы можете использовать preg_replace_callback:
$s = preg_replace_callback('~(MNO)(.*?)(XYZ)~s', function($m) {
return $m[1] . str_replace('ABC', 'XXX', $m[2]) . $m[3];
}, $s);
Или, с обходами, чтобы сделать код внутри анонимной функции немного компактнее:
$s = preg_replace_callback('~(?<=MNO).*?(?=XYZ)~s', function($m) {
return str_replace('ABC', 'XXX', $m[0]);
}, $s);
См. демонстрация PHP
Здесь (MNO)(.*?)(XYZ) соответствует и захватывает MNO, все между MNO и XYZ
а затем XYZ на три группы и внутри анонимной функции все ABC заменяются только во второй группе. Обратите внимание, что флаг s в конце регулярного выражения также необходим, чтобы . соответствовал символам разрыва строки.
Во втором примере (?<=MNO) — это положительный просмотр назад, который не использует текст и требует, чтобы MNO присутствовал непосредственно слева от текущего местоположения, а (?=XYZ) — это положительный просмотр, который требует, чтобы XYZ присутствовал сразу же справа от текущего местоположения. и не потребляет текст, поэтому здесь нет необходимости в группах.
С preg_replace намного сложнее:
preg_replace('~(?:\G(?!\A)|MNO)(?:(?!MNO).)*?\KABC(?=(?:(?!MNO).)*?XYZ)~s', 'XXX', $s)
См. демонстрация регулярных выражений.
Подробности
(?:\G(?!\A)|MNO) - конец предыдущего матча или MNO(?:(?!MNO).)*? - любой 1 символ, 0 или более вхождений, как можно меньше, которые не начинают последовательность MNO символов\K - оператор сброса совпадения, который отбрасывает текст в буфере совпаденияABC - ABC(?=(?:(?!MNO).)*?XYZ) - сразу справа должно быть 0+ символов, как можно меньше, которые не начинают последовательность символов MNO, за которой следует XYZ.В этом случае он работает с более чем одним вхождением MNO и XZY. Пока не нужно, но при необходимости буду иметь в виду. Большое спасибо.
Обязательно ли использовать
preg_replaceили можно использовать иpreg_replace_callback?