正则表达式的单词边界在PHP中是如何工作的


How exactly do Regular Expression word boundaries work in PHP?

我目前正在编写一个库,用于匹配内容中的特定单词。

从本质上讲,它的工作方式是将单词编译为正则表达式,并通过所述正则表达式运行内容。

我想添加的一个功能是指定要匹配的给定单词必须以单词开头和/或结尾。例如,我有单词cat。我指定它必须以单词开头,因此catering匹配,因为cat在开头,但ducat将不匹配cat不以单词开头。

我想使用单词边界来完成这项工作,但在一些测试中,我发现它并没有像我预期的那样工作

采取以下措施,

preg_match("/(^|'b)@nimal/i", "something@nimal", $match);
preg_match("/(^|'b)@nimal/i", "something!@nimal", $match);

在上面的陈述中,我希望得到以下结果,

> false
> 1 (@nimal)

但结果恰恰相反,

> 1 (@nimal)
> false

在第一场比赛中,我预计它会失败,因为小组将吃掉@,让nimal对阵@nimal,但显然没有。相反,该组匹配一个空字符串,因此@nimal被匹配,这意味着@被认为是单词的一部分。

在第二个例子中,我希望这群人吃!,留下@nimal来匹配其余的(它应该这样做)。相反,它似乎将!@组合在一起形成一个单词,这通过以下匹配来确认

preg_match("/g'b!@'bn/i", "something!@nimal", $match);

你知道正则表达式为什么会这样做吗?

我只是喜欢一个清楚地记录单词边界是如何确定的页面,但我一生都找不到一个。

单词边界'b在从'w(单词字符)更改为'W(非单词字符)时匹配。如果在@之前有一个'b,即'W字符,则需要进行匹配。因此,为了匹配,您需要在@ 之前添加一个单词字符

something@nimal
        ^^

===>由于g@之间的字边界而匹配。

something!@nimal
         ^^ 

===>不匹配,因为!@之间没有字边界,两个字符都是'W

我在进行类似匹配时遇到的一个问题是像can'tit's这样的单词,其中撇号被认为是单词/非单词边界(因为它是由'W而不是'w匹配的)。如果这对你来说可能是个问题,你应该排除撇号(以及有时出现的所有变体,如"answers"),例如创建一个类,例如['b^']

您可能还会遇到真正属于单词一部分的UTF8字符的问题(即我们人类对单词的意思),例如,根据如何编码单词(如Svašek)来测试您的正则表达式。

因此,在解析正常的"语言"文本时,通常更容易找到"语言"边界,如空格字符(不仅是字面上的空格,还有包括换行符和制表符在内的完整类)、逗号、冒号、句号等(如果解析HTML,还可以使用尖括号)。YMMV。

@不是单词字符的一部分(但是,在您的语言环境中,默认情况下它可能是"单词"字符是任何字母或数字或下划线字符,Source-,因此@不是word字符,因此不是'w而是'W,并且链接的任何'w'W'W'w组合都标记'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"
}