我有一个PHP突出显示功能,可以使某些单词加粗。
下面是函数,它很有用,除非数组:$ words包含单个值:b
例如有人搜索:jessie j price tag feat b o b
这将在数组$ words中包含以下条目:jessie,j,price,tag,feat,b,o,b
当'b'出现时,我的整个功能出错了,它显示了一大堆错误的html标签。当然,我可以从数组中删除任何“b”值,但这并不理想,因为突出显示在某些查询中不起作用。
此示例脚本:
function highlightWords2($text, $words)
{
$text = ($text);
foreach ($words as $word)
{
$word = preg_quote($word);
$text = preg_replace("/\b($word)\b/i", '<b>$1</b>', $text);
}
return $text;
}
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
echo highlightWords2($string, $words);
将输出:
<<<b>b</b>><b>b</b></<b>b</b>>>jessie</<<b>b</b>><b>b</b></<b>b</b>>> j price <<<b>b</b>><b>b</b></<b>b</b>>>tag</<<b>b</b>><b>b</b></<b>b</b>>> feat <<b>b</b>><b>b</b></<b>b</b>> <<b>b</b>>o</<b>b</b>> <<b>b</b>><b>b</b></<b>b</b>>
这只会发生,因为数组中有“b”。
你能看到我可以改变的任何东西让它正常工作吗?
答案 0 :(得分:5)
你的问题是,当你的功能通过并查找所有b为粗体时,它会看到粗体标签,并尝试加粗它们。
@symcbean已经接近但忘了一件事。
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
print hl($string, $words);
function hl($inp, $words)
{
$replace=array_flip(array_flip($words)); // remove duplicates
$pattern=array();
foreach ($replace as $k=>$fword) {
$pattern[]='/\b(' . $fword . ')(?!>)\b/i';
$replace[$k]='<b>$1</b>';
}
return preg_replace($pattern, $replace, $inp);
}
你是否看到这添加了“(?!&gt;)”这是一个负面的预测断言,基本上它表示如果字符串后面没有“&gt;”则匹配可以看到的是打开粗体和关闭粗体标签。注意我只检查“&gt;”在字符串之后为了排除开始和结束粗体标记,因为在字符串的开头查找它不会捕获关闭粗体标记。上面的代码完全符合预期。
答案 1 :(得分:2)
您的基本问题是您非常频繁地替换HTML中的纯文本字符串。当你替换标签和属性中的文本时,这确实会导致小字符串的问题。
相反,您需要应用搜索并仅替换HTML文本之间的文本。此外,您也不想在另一个高亮显示内部突出显示。
要做这些事情,正则表达式非常有限。而是在PHP中使用HTML解析器,例如DOMDocument
。使用HTML解析器,可以仅在HTML文本元素内部进行搜索(而不是在标签,属性和注释等其他内容)。
您可以在a previous answer of mine中找到文字的荧光笔,并详细说明其工作原理。问题是 Ignore html tags in preg_replace ,它与您的问题非常相似,因此可能此代码段很有用,它使用<span>
代替<b>
代码:
$doc = new DOMDocument;
$doc->loadXML($str);
$xp = new DOMXPath($doc);
$anchor = $doc->getElementsByTagName('body')->item(0);
if (!$anchor)
{
throw new Exception('Anchor element not found.');
}
// search elements that contain the search-text
$r = $xp->query('//*[contains(., "'.$search.'")]/*[FALSE = contains(., "'.$search.'")]/..', $anchor);
if (!$r)
{
throw new Exception('XPath failed.');
}
// process search results
foreach($r as $i => $node)
{
$textNodes = $xp->query('.//child::text()', $node);
// extract $search textnode ranges, create fitting nodes if necessary
$range = new TextRange($textNodes);
$ranges = array();
while(FALSE !== $start = strpos($range, $search))
{
$base = $range->split($start);
$range = $base->split(strlen($search));
$ranges[] = $base;
};
// wrap every each matching textnode
foreach($ranges as $range)
{
foreach($range->getNodes() as $node)
{
$span = $doc->createElement('span');
$span->setAttribute('class', 'search_hightlight');
$node = $node->parentNode->replaceChild($span, $node);
$span->appendChild($node);
}
}
}
如果你将它用于多个搜索词,我会根据搜索词添加一个带有数字的附加类,这样你就可以用不同颜色的CSS很好地设置它的样式。
此外,您应删除重复的搜索词并使xpath表达式识别为不查找已经指定了高亮区域的元素的文本。
答案 2 :(得分:0)
如果是我,我会使用javascript。
但是使用PHP,因为问题似乎只是搜索中的重复条目,只需删除它们,也可以只运行一次preg_replace而不是多次....
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
print hl($string, $words);
function hl($inp, $words)
{
$replace=array_flip(array_flip($words)); // remove duplicates
$pattern=array();
foreach ($replace as $k=>$fword) {
$pattern[]='/\b(' . $fword . ')\b/i';
$replace[$k]='<b>$1<b>';
}
return preg_replace($pattern, $replace, $inp);
}