【问题标题】:preg_match_all: get text inside quotes except in html tagspreg_match_all:获取引号内的文本,除了 html 标签
【发布时间】:2013-09-25 14:19:19
【问题描述】:

我最近使用了一种模式,用开/关双引号对替换直双引号。

$string = preg_replace('/(\")([^\"]+)(\")/','“$2”',$string);

当 $string 是一个句子,甚至是一个段落时,它也能正常工作。

但是……

我的函数可以被调用来处理一大块 HTML 代码,但它不再像例外一样工作了:

$string    = preg_replace('/(\")([^\"]+)(\")/','“$2”','<a href="page.html">Something "with" quotes</a>');

返回

<a href=“page.html”>Something “with” quotes</a>

这是个问题……

所以我想我可以分两次完成:提取标签中的文本,然后替换引号。

我试过了

$pattern='/<[^>]+>(.*)<\/[^>]+>/';

如果字符串是

,它就可以工作
$string='<a href="page.html">Something "with" quotes</a>';

但它不适用于以下字符串:

$string='Something "with" quotes <a href="page.html">Something "with" quotes</a>';

有什么想法吗?

伯特兰

【问题讨论】:

  • @Kolink 我知道这会出现。这就是为什么我建议使用 simplexml 并且只将其应用于文本而不是属性。
  • 我必须“清理”的字符串在 90% 的情况下是文本字段的值,在某些情况下,您在其中有“位”的 html 代码。这就是解析不合适的原因。
  • 'Something "with quotes &lt;a href="page.html"&gt;Something "with" quotes&lt;/a&gt;' 的情况下,期望的输出应该是什么? 'Something “with quotes &lt;a href="page.html"&gt;Something ”with" quotes&lt;/a&gt;''Something "with quotes &lt;a href="page.html"&gt;Something “with” quotes&lt;/a&gt;' ?

标签: html regex tags quotes


【解决方案1】:

我猜通常回复...因为它已经是pointed out,您不应该通过正则表达式解析HTML。您可以查看PHP Simple DOM Parse 以提取文本并应用您的正则表达式,从您已经说过的内容来看,这似乎工作得很好。

This 教程应该让你朝着正确的方向前进。

【讨论】:

  • 谢谢,但是当我需要解析一些代码时,我正在使用解析器。在这种情况下,解析代码不会帮助我用其他字符替换某些字符。
【解决方案2】:

我很确定这将在一场激烈的战争中结束,但这很有效:

echo do_replace('<a href="page.html">Something "with" quotes</a>')."\n";
echo do_replace('Something "with" quotes <a href="page.html">Something "with" quotes</a>')."\n";

function do_replace($string){
    preg_match_all('/<([^"]*?|"[^"]*")*>/', $string, $matches);
    $matches = array_flip($matches[0]);

    $uuid = md5(mt_rand());
    while(strpos($string, $uuid) !== false) $uuid = md5(mt_rand()); 
    // if you want better (time) garanties you could build a prefix tree and search it for a string not in it (would be O(n)

    foreach($matches as $key => $value)
        $matches[$key] = $uuid.$value;

    $string = str_replace(array_keys($matches), $matches, $string);
    $string = preg_replace('/\"([^\"<]+)\"/','&ldquo;$1&rdquo;', $string);
    return str_replace($matches, array_keys($matches), $string);
}

输出(我将 “ 和 ” 替换为 ):

<a href="page.html">Something “with” quotes</a>
Something “with” quotes <a href="page.html">Something “with” quotes</a>

使用 costum 状态机,您甚至可以在没有第一次替换的情况下完成它,然后再替换回来。我还是建议使用 Parser。

【讨论】:

  • 我试过了,效果很好。谢谢。问题是,在 90% 的情况下,它只是我得到的一个字符串(来自文本输入的值),并且使用解析器处理一个字符串或几个标签实际上需要更多的工作。此正则表达式不适用于完整的 html 页面。
【解决方案3】:

我终于找到了办法:

  1. 提取可以在任何标签(如果有)内部或外部(之前、之后)的文本
  2. 使用回调逐对查找引号并替换它们。

代码

$string = preg_replace_callback('/[^<>]*(?!([^<]+)?>)/sim', create_function('$matches',  'return preg_replace(\'/(\")([^\"]+)(\")/\', \'“$2”\', $matches[0]);'), $string);

【讨论】:

    【解决方案4】:

    Bertrand,重新提出这个问题,因为它有一个简单的解决方案,可以让您一次性完成替换 - 无需回调。 (在针对how to exclude patterns in regex 的一般问题进行一些研究时发现了您的问题。)

    这是我们的简单正则表达式:

    <[^>]*>(*SKIP)(*F)|"([^"]*)"
    

    交替匹配的左侧完成&lt;tags&gt; 然后故意失败。右侧匹配双引号字符串,我们知道它们是正确的字符串,因为它们没有被左侧的表达式匹配。

    这段代码展示了如何使用正则表达式(查看online demo底部的结果):

    <?php
    $regex = '~<[^>]*>(*SKIP)(*F)|"([^"]*)"~';
    $subject = 'Something "with" quotes <a href="page.html">Something "with" quotes</a>';
    $replaced = preg_replace($regex,"“$1”",$subject);
    echo $replaced."<br />\n";
    ?>
    

    参考

    How to match (or replace) a pattern except in situations s1, s2, s3...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      相关资源
      最近更新 更多