Как точно работают границы слова регулярного выражения в 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"
}