问题 反向htmlentities / html_entity_decode


基本上我想要像这样转换一个字符串:

<code> &lt;div&gt; blabla &lt;/div&gt; </code>

进入这个:

&lt;code&gt; <div> blabla </div> &lt;/code&gt;

我该怎么做?


用例(某些人很好奇):

像这样的页面 这个 包含允许的HTML标记和示例列表。例如, <code> 是一个允许的标签,这将是样本:

<code>&lt;?php echo "Hello World!"; ?&gt;</code>

我想要一个反向函数,因为有许多这样的标签带有样本,我将它们全部存储到一个数组中,我在一个循环中迭代,而不是单独处理每个...


1451
2017-07-12 17:20


起源

源字符串是否始终采用相同的格式:包含在HTML实体编码的字符串中 <code> 标签?或者您是否需要一个可以处理更多通用情况的解决方案,其中编码和非编码字符可能混合在一起? - Mike
有趣的问题! - Lightness Races in Orbit
确实有趣。你能提供一个用例吗?只是好奇... - aercolino


答案:


我的版本使用正则表达式:

$string = '<code> &lt;div&gt; blabla &lt;/div&gt; </code>';
$new_string = preg_replace(
    '/(.*?)(<.*?>|$)/se', 
    'html_entity_decode("$1").htmlentities("$2")', 
    $string
);

它试图匹配每一个 标签 和 textnode 然后申请 ヶ辆 和 html_entity_decode 分别。


4
2017-07-17 11:33



很干净的解决方案 - aercolino
谢谢。我不知道我应该选择哪种方法,你或adlawson的:) - Alex
@Alex欢迎您!两种方法都有其自身的副作用。您需要测试哪种情况最适合您的情况。 - Karolis


没有现有的功能,但看看这个。 到目前为止,我只在你的例子中测试了它,但是这个函数应该可以工作 所有 ヶ辆

function html_entity_invert($string) {
    $matches = $store = array();
    preg_match_all('/(&(#?\w){2,6};)/', $string, $matches, PREG_SET_ORDER);

    foreach ($matches as $i => $match) {
        $key = '__STORED_ENTITY_' . $i . '__';
        $store[$key] = html_entity_decode($match[0]);
        $string = str_replace($match[0], $key, $string);
    }

    return str_replace(array_keys($store), $store, htmlentities($string));
}

更新:

  • 谢谢 @麦克风 花时间用其他字符串测试我的函数。我已经更新了我的正则表达式 /(\&(.+)\;)/ 至 /(\&([^\&\;]+)\;)/ 应该照顾他提出的问题。

  • 我还补充道 {2,6} 限制每场比赛的长度,以减少误报的可能性。

  • 改变了正则表达式 /(\&([^\&\;]+){2,6}\;)/ 至 /(&([^&;]+){2,6};)/ 删除不必要的兴奋。

  • 哇,脑波!改变了正则表达式 /(&([^&;]+){2,6};)/ 至 /(&(#?\w){2,6};)/ 减少误报的可能性 更深入


4
2017-07-16 14:45



adlawson ++这是一个非常巧妙的解决方案。 - Mike
您可以使用非常相似的代码一步完成相同的反转。 - Karolis
我不确定你将如何达到同样的结果。我最接近的是使用 return preg_replace('/(&(#?\w){2,6};)([^&;]*)/', html_entity_decode("$1") . htmlentities("$2"), $string);,但它不起作用。与单个正则表达式相比,单独解决每个问题要简单得多。 - adlawson
@adlawson为什么你认为正则表达式更难?顺便说一下你也使用正则表达式:)关于正则表达式的常见问题是可读性。但在这种情况下,正则表达式很短,甚至更快。例如,您的所有代码都可以重写为: return preg_replace('/(.*?)(&(#?\w){2,6};|$)/se', 'htmlentities("$1").html_entity_decode("$2")', $string); - Karolis
顺便一提 &(#?\w){2,6}; 匹配html实体不是很好,因为它会匹配 &ab#cd;,但它不匹配 &thetasym;。我认为 &#?\w+; 或类似的东西会更好。 - Karolis


单独更换对你来说不够好。无论是正则表达式还是简单的字符串替换,因为如果您替换&lt; gt符号然后<和>符号,反之亦然,您将最终得到一个编码/解码(所有&lt;&gt;或所有<和>符号)。

因此,如果你想这样做,你将需要解析一套(我选择用占位符替换)做一个替换然后把它们放回去做另一个替换。

$str = "<code> &lt;div&gt; blabla &lt;/div&gt; </code>";
$search = array("&lt;","&gt;",);

//place holder for &lt; and &gt;
$replace = array("[","]");

//first replace to sub out &lt; and &gt; for [ and ] respectively
$str = str_replace($search, $replace, $str);

//second replace to get rid of original < and >
$search = array("<",">");
$replace = array("&lt;","&gt;",);
$str = str_replace($search, $replace, $str);

//third replace to turn [ and ] into < and >
$search = array("[","]");
$replace = array("<",">");

$str = str_replace($search, $replace, $str);

echo $str;

1
2017-07-12 18:46



我希望有一个内置函数,或者至少需要更少的代码来完成它。我不喜欢处理每个角色的转换的想法...... - Alex
如果有内置功能,我不知道它是什么。您可以将它设为一个函数,并在需要时以字符串作为参数调用它。它真的不是那么多代码,而且所有操作都很快。我认为你的愿望对于内置函数来说有点特定,但那就是用户定义函数的要点。 - Aaron Ray
这里没有内置函数,因为问题很有趣,我在这里看不到任何用例,它既不是完整的边缘情况,也不是理论上的,或者是一种可怕的代码味道。 - Lightness Races in Orbit


我想我有一个小的解决方案,为什么不将html标签分解成一个数组,然后根据需要进行比较和更改?

function invertHTML($str) {
    $res = array();
    for ($i=0, $j=0; $i < strlen($str); $i++) { 
        if ($str{$i} == "<") { 
           if (isset($res[$j]) && strlen($res[$j]) > 0){
                $j++; 
                $res[$j] = '';
           } else {
               $res[$j] = '';
           }
           $pos = strpos($str, ">", $i); 
           $res[$j] .= substr($str, $i, $pos - $i+1); 
           $i += ($pos - $i); 
           $j++;
           $res[$j] = '';
           continue; 
        } 
        $res[$j] .= $str{$i}; 
    } 

    $newString = '';
    foreach($res as $html){
        $change = html_entity_decode($html);
        if($change != $html){
            $newString .= $change;
        } else {
            $newString .= htmlentities($html);
        }
    }
    return $newString; 
}

修改....没有错误。


1
2017-07-17 07:14



首先 for 循环遍历字符串中的每个字符并且脱离上下文。这将打破像字符串 <div>5 > 1</div>。而且,它最好通过 strlen($string) 通过引用,否则它重新计算每次迭代的长度。 - adlawson
我刚刚用OP的字符串测试它,用“5> 1”测试它,你得到了 Undefined offset: $i 在这两种情况下。 - adlawson
修改,没有错误,通过....如果你测试一个10k大的字符串你将得到比我的脚本快2.3倍的结果 preg_match。 - Mihai Iorga
虽然您的功能可能会稍微快一点,但我的功能却具有反转功能 任何 html实体(&raquo;,&mdash,&heart等)。这是strlen in for ($i=0, $j=0; $i < strlen($str); $i++) { 我的意思应该通过引用传递。 - adlawson
你确定我的例子不会转换任何html实体吗? - Mihai Iorga


所以,虽然这里的其他人推荐了正则表达式,这可能是绝对正确的方式...我想发布这个,因为它足以满足你提出的问题。

假设你总是使用html'esque代码:

 $str = '<code> &lt;div&gt; blabla &lt;/div&gt; </code>';
 xml_parse_into_struct(xml_parser_create(), $str, $nodes);
 $xmlArr = array();
 foreach($nodes as $node) { 
     echo htmlentities('<' . $node['tag'] . '>') . html_entity_decode($node['value']) . htmlentities('</' . $node['tag'] . '>');
 }

给我以下输出:

&lt;CODE&gt; <div> blabla </div> &lt;/CODE&gt;

相当肯定这不会支持再次倒退..正如其他解决方案所发布的那样,意思是:

 $orig = '<code> &lt;div&gt; blabla &lt;/div&gt; </code>';
 $modified = '&lt;CODE&gt; <div> blabla </div> &lt;/CODE&gt;';
 $modifiedAgain = '<code> &lt;div&gt; blabla &lt;/div&gt; </code>';

1
2017-07-17 12:03





我建议使用正则表达式,例如的preg_replace():


0
2017-07-12 17:33



呕吐“正常表达!”并且在每个人的正则表达式上释放任意链接 不是答案。 - Lightness Races in Orbit


编辑:似乎我还没有完全回答你的问题。没有内置的PHP函数可以执行您想要的操作,但您可以使用正则表达式甚至简单表达式查找和替换: str_replace函数的preg_replace


0
2017-07-12 17:26



是什么让你思考 print 将解码实体?而且,这并不能解决实际问题。 - Matt
@Matt OP也可以使用html_entity_decode。请参阅编辑以获取第二条评论。 - wanovak
我不明白你为什么要使用缓冲区。 $var 是相同的 $out 在你的例子中。 - Matt
没想到会。编辑以删除虚假信息。 - wanovak
这甚至没有接近回答任何问题。 - Lightness Races in Orbit