1

我正在使用在这里找到的正则表达式替换一些文本。

$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>';

=> 第二个:)应该被忽略。

4

2 回答 2

1

首先预过滤您的输入字符串。清理 HTML 标记中的所有笑脸:

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

并在上面运行您的代码。

于 2012-05-22T17:28:02.957 回答
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] );

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

于 2012-05-22T20:10:28.370 回答