Как точно работают границы слова регулярного выражения в PHP?

В настоящее время я пишу библиотеку для соответствия определенным словам в контенте.

По существу, он работает, компилируя слова в регулярные выражения и запуская содержимое через указанные регулярные выражения.

Функция, которую я хочу добавить, указывает, должно ли заданное слово совпадать должно начинаться и/или заканчивать слово. Например, у меня есть слово cat. Я указываю, что он должен начинать слово, поэтому catering будет соответствовать, поскольку cat находится в начале, но ducat не будет соответствовать, поскольку cat не запускает слово.

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

Возьмем следующее:

preg_match("/(^|\b)@nimal/i", "[email protected]", $match);
preg_match("/(^|\b)@nimal/i", "[email protected]", $match);

В приведенных выше утверждениях я ожидал бы следующих результатов:

> false
> 1 (@nimal)

Но вместо этого результат противоположный,

> 1 (@nimal)
> false

Во-первых, я ожидаю, что он потерпит неудачу, так как группа будет есть @, оставив nimal для соответствия с @nimal, чего, очевидно, нет. Вместо этого группа соответствует пустой строке, поэтому @nimal соответствует, что означает, что @ считается частью слова.

Во втором случае я ожидаю, что группа съедет !, оставив @nimal, чтобы соответствовать остальным (что должно). Вместо этого он объединяется вместе ! и @, чтобы сформировать слово, что подтверждается следующим сопоставлением

preg_match("/g\[email protected]\bn/i", "[email protected]", $match);

Любые идеи, почему это делает регулярное выражение?

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

Ответ 1

Граница слов \b совпадает с изменением от \w (символа слова) до символа \w неслов. Вы хотите совместить, если есть \b до вашего @, который является символом \w. Поэтому для соответствия вам нужен символ слова перед вашим @

[email protected]
        ^^

== > Сопоставьте из-за границы слова между g и @.

so[email protected]
         ^^ 

== > НЕТ, потому что между ! и @ нет границы слова, оба символа \w

Ответ 2

Одна из проблем, с которыми я столкнулся, выполняет аналогичное сопоставление: слова can't и it's, где апостроф считается границей слова/не-слова (поскольку он соответствует \W, а не \W), Если это может быть проблемой для вас, вы должны исключить апостроф (и все варианты, такие как и "иногда появляющиеся" ), например, путем создания класса, например. [\b^'].

У вас могут также возникнуть проблемы с символами UTF8, которые действительно являются частью слова (то есть, что мы, люди, подразумеваем под словом), например, проверьте ваше регулярное выражение на то, как вы кодируете слово, например Svašek.

Поэтому часто при анализе обычного "лингвистического" текста искать "лингвистические" границы, такие как символы пробела (а не просто буквально пробелы, но полный класс, включая новые строки и вкладки), запятые, двоеточия, полные остановки, и т.д. (и угловые скобки, если вы разбираете HTML). YMMV.

Ответ 3

@ не является частью символа слова (в вашей локали, вероятно, это, однако, по по умолчанию. Символом "слово" является любая буква или цифра или символ подчеркивания, Источник - , поэтому @ не является символом word, поэтому не \w, а \w и как ссылка \w\W или \w\W комбинация отмечает a \b позиция), поэтому всегда соответствует границе слова (в регулярном выражении OP).

Следующее похоже на ваши регулярные выражения с той разницей, что вместо @ используется a. И начало строки также является границей слов, поэтому нет необходимости также ее указывать:

$r = preg_match("/\b(animal)/i", "somethinganimal", $match);
var_dump($r, $match);

$r = preg_match("/\b(animal)/i", "something!animal", $match);
var_dump($r, $match);

Вывод:

int(0)
array(0) {
}
int(1)
array(2) {
  [0]=>
  string(6) "animal"
  [1]=>
  string(6) "animal"
}