Почему `=` или `` `ломает PHP regexp при использовании`\b`?

это продолжение после чтения Как указать "Пробел или конец строки" . и "пробел или начало строки" ,

Оттуда оно означает, что нужно совместить слово во фразе. Я могу даже добавить несколько других решений. Но как только добавляется = или ", он перестает работать. Почему?

Я собираюсь выполнить поиск stackoverflow и заменить его на OK с помощью preg_replace()

preg_replace('/\bstackoverflow\b/', 'OK', $input_line)

input:
1: stackoverflow xxx
2: xxx stackoverflow xxx
3: xxx stackoverflow
result:
1: OK xxx
2: xxx OK xxx
3: xxx OK

теперь, если я меняю его на соответствие stackoverflow="", он перестает работать.

preg_replace('/\bstackoverflow=""\b/', 'OK', $input_line)

input:
1: stackoverflow="" xxx
2: xxx stackoverflow="" xxx
3: xxx stackoverflow=""
result:
1: stackoverflow="" xxx
2: xxx stackoverflow="" xxx
3: xxx stackoverflow=""

то же самое произойдет, если я буду использовать в своем регулярном выражении: /\bstackoverflow=\b/ или /\bstackoverflow"\b/. Я уже проверил руководство, если = или " являются специальными символами, это не так. но я даже попробовал /\bstackoverflow\=\"\"\b/

Почему это?

в этом примере удаление \b также решит его, но оно также будет соответствовать nostackoverflow=""not, которое я не хочу.

я также пробовал альтернативы \b, такие как [ ^] и ( |^). Интересно, что [ ^] (пробел или начало строки) не будет работать для начала строки, только пробел. Но ( |^) будет отлично работать для обоих.

Ответ 1

Фон

Из regular-expressions.info Страница границ Word:

Метасимвол \b представляет собой привязку, подобную знаку каретки и знака доллара. Он соответствует позиции, которая называется "границей слов". Это совпадение с нулевой длиной.

Существуют три разных позиции, которые соответствуют границам слов:
 - Перед первым символом в строке, если первый символ является символом слова.
 - После последнего символа в строке, если последний символ является символом слова.
 - Между двумя символами в строке, где один является символом слова, а другой - не символом слова.

Очень хорошее объяснение из nhahtdh post:

Граница слов \b эквивалентна:

(?:(?<!\w)(?=\w)|(?<=\w)(?!\w))

Это означает:

  • Вперёд, по крайней мере, есть символ, который является символом слова, и, мы не можем найти символ слова (либо символ не является символом слова, или это начало строки).

    ИЛИ

  • Прямо за ним, по крайней мере, есть символ, который является символом слова и, мы не можем найти символ слова (либо символ не является символом слова, или это конец строки).

Что не так с вашим регулярным выражением

Причина, по которой \b не подходит, заключается в том, что она требует, чтобы символ слова/не-слова появлялся после/до него, что зависит от непосредственного контекста с обеих сторон от \b. Когда вы динамически создаете регулярное выражение, вы не знаете, какой из них использовать, \b или \b. Для вашего случая вы можете использовать '/\bstackoverflow=""\B/', но для этого потребуется добавление умного слова/не-слова. Однако есть более простой способ: использовать негативные образы.

Решение

(?<!\w)stackoverflow=""(?!\w)

Смотрите демо-версия regex

Регулярное выражение содержит отрицательные образы вместо границ слов. (?<!\w) lookbehind не соответствует совпадению, если перед stackoverflow="" есть символ слова, а (?!\w) lookahead терпит неудачу, если за stackoverflow="" следует символ слова.

То, что соответствует сокращенному символу слов \w, зависит от того, включен ли модификатор Unicode /u. Без него a \w соответствует только [a-zA-Z0-9_]. Вы можете использовать дополнительные ограничения, используя образы.

Demo

Демо-версия PHP:

$re = '/(?<!\w)stackoverflow=""(?!\w)/'; 
$str = ",stackoverflow=\"\" xxx\nxxx stackoverflow=\"\" xxx\nxxx stackoverflow=\"\"\nstackoverflow=\"\" xxx"; 
echo preg_replace($re, "NEW=\"\"", $str);

Ответ 2

Проблема заключается в том, что вы используете \b, который является "границей слов". Это заполнитель для (^\w|\w$|\W\w|\w\W), где \w является символом "слова" [A-Za-z0-9_] и \w является противоположным. Проблема в том, что a " не соответствует символам "слова", поэтому граничное условие не выполняется.

Попробуйте вместо этого использовать \s, который будет соответствовать любому символу пробела.

(?:^|\s)stackoverflow=""(?:\s|$)

Символы внутри класса не интерпретируются, кроме ^, используемых как оператор отрицания в начале класса, и - как оператор диапазона. Вот почему [ ^] не сработает для вас. Он искал литерал ^.

$ php -a
Interactive shell

php > $input_line='
php ' stackoverflow="" xxx
php ' xxx stackoverflow="" xxx
php ' xxx stackoverflow=""
php ' ';
php > echo preg_replace('/(?:^|\s)stackoverflow=""(?:\s|$)/', 'OK', $input_line);
OKxxx
xxxOKxxx
xxxOK

https://regex101.com/r/nP2aB8/1

Ответ 3

", конечно, не является особенным.

Граница слов, \b, OTOH, есть. Он ищет начало/конец слова, а на границе он ожидает символ слова - и цитата не является таким символом.

Удалите его с конца или замените его отрицательным поиском в поисках слова.