preg replace 在检测单词时会忽略非字母字符

preg replace would ignore non-letter characters when detecting words

我有一个单词数组和一个字符串,我想为字符串中的单词添加一个主题标签,以表明它们在数组中具有匹配项。我使用这个循环来查找和替换单词:

foreach($testArray as $tag){
   $str = preg_replace("~\b".$tag."~i","#$0",$str);
}

问题:假设我的数组中有单词 "is" 和 "isolate"。我将在输出中得到##isolate。这意味着单词 "isolate" 在 "is" 中找到一次,在 "isolate" 中找到一次。并且该模式忽略了“#isoldated”不再以 "is" 开头而是以“#”开头的事实。

我举了一个例子,但这只是一个例子e,我不想只解决这个例子,而是想解决所有其他的可能性:

$str = "this is isolated is an  example of this and that";
$testArray = array('is','isolated','somethingElse');

输出将是:

this #is ##isolated #is an  example of this and that

您可以构建一个正则表达式,其中交替组的两端都包含单词边界,并一次性替换所有匹配项:

$str = "this is isolated is an  example of this and that";
$testArray = array('is','isolated','somethingElse');
echo preg_replace('~\b(?:' . implode('|', $testArray) . ')\b~i', '#[=10=]', $str);
// => this #is #isolated #is an  example of this and that

参见PHP demo

正则表达式看起来像

~\b(?:is|isolated|somethingElse)\b~

查看其online demo

如果你想让你的方法奏效,你可以在 \b 之后添加一个负面回顾:"~\b(?<!#)".$tag."~i","#$0"。 lookbehind 将使所有以 # 开头的匹配失败。参见 this PHP demo

这样做的一种方法是按单词拆分字符串并使用原始单词数组构建关联数组(避免使用 in_array):

$str = "this is isolated is an example of this and that";
$testArray = array('is','isolated','somethingElse');

$hash = array_flip(array_map('strtolower', $testArray));

$parts = preg_split('~\b~', $str);

for ($i=1; $i<count($parts); $i+=2) {
    $low = strtolower($parts[$i]);
    if (isset($hash[$low])) $parts[$i-1] .= '#';
}

$result = implode('', $parts);

echo $result;

这样,无论数组中的单词数是多少,您的字符串只被处理一次。