忽略HTML标记定义中的匹配项

时间:2012-05-22 16:55:48

标签: php regex string

我正在使用我发现here的正则表达式替换一些文本。

$items = array(
  ':)'   => 'smile',
  ':('   => 'sad',
  '=))'  => 'laugh',
  ':p'   => 'tongue',      
); 

foreach($items as $key => $class)
  $regex[] = preg_quote($key, '#');

$regex = '#(?!<\w)('.implode('|', $regex).')(?!\w)#';

$string = preg_replace_callback($regex, function($matches) use($items){

  if(isset($items[$matches[0]])) 
    return '<span class="'.$items[$matches[0]].'">'.$matches[0].'</span>';

  return $matches[0];

}, $string);

它有效,但如何忽略HTML标记定义中的匹配(如标记属性内)?

例如:

$string = 'Hello :) <a title="Hello :)"> Bye :( </a>';

=&GT;第二个:)应该被忽略。

2 个答案:

答案 0 :(得分:1)

首先预先过滤输入字符串。清理HTML标记中的任何表情符号:

$regex = '#<[^>]+('.implode('|', $regex).')[^>]+>#';

并运行上面的代码。

答案 1 :(得分:1)

这是一个基于DOMDocument的实现,可以为您的HTML替换书籍字符串:

$string = 'Hello :) <a title="Hello :)"> Bye :( </a>';

$items = array(
  ':)'   => 'smile',
  ':('   => 'sad',
  '=))'  => 'laugh',
  ':p'   => 'tongue',      
); 

foreach($items as $key => $class) $regex[] = preg_quote($key);

$regex = '#(?!<\w)('.implode('|', $regex).')(?!\w)#';

$doc = new DOMDocument();
$doc->loadHTML($string);

$xp = new DOMXPath($doc);

$text_nodes = $xp->query('//text()');

foreach ($text_nodes as $text_node)
{
  $parent  = $text_node->parentNode;
  $context = $text_node->nextSibling;
  $text    = $text_node->nodeValue;
  $matches = array();
  $offset  = 0;

  $parent->removeChild($text_node);

  while ( preg_match($regex, $text, $matches, PREG_OFFSET_CAPTURE, $offset) > 0 )
  {
    $match  = $matches[0];
    $smiley = $match[0];
    $pos    = $match[1];
    $prefix = substr($text, $offset, $pos - $offset);
    $offset = $pos + strlen($smiley);

    $span = $doc->createElement('span', $smiley);
    $span->setAttribute('class', $items[$smiley]);

    $parent->insertBefore( $doc->createTextNode($prefix), $context );
    $parent->insertBefore( $span, $context );
  }

  $suffix = substr($text, $offset);
  $parent->insertBefore( $doc->createTextNode($suffix), $context );
}

$body = $doc->getElementsByTagName('body');
$html = $doc->saveHTML( $body[0] );

将它包裹在一个函数中,你很高兴。它可能比正则表达式更多的代码行,但它不是一个丑陋的,错误的维护噩梦(就像任何基于正则表达式的解决方案一样)。