【问题标题】:PHP preg_replace Confusing errorPHP preg_replace 令人困惑的错误
【发布时间】:2015-05-30 12:37:31
【问题描述】:

我有一个非常奇怪的问题,我花了很多时间但没有任何成功......我的网站上有一个内容可编辑区域,用户可以在其中选择他们可以在书面文本中立即看到的表情符号(在内容可编辑区域的情况下)。因此,对于从用户到用户的消息,我不关心文本的长度,但对于编写 cmets,我关心!我需要计算字符串的所有字符。

现在我遇到了表情符号这样传输的问题:

<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon emoticon-class-name-for-example-happy">

好吧,当然我只想为每个表情符号计算 1 个字符,所以我编写了一个正则表达式并尝试用“1”替换所有表情符号。之后我认为只需 strlen 就很容易得到使用的字符数。 但这仅在理论上有效,但该死的为什么...... .

所以我的正则表达式是:

<img[ ]src=["'].+?["'][ ]class=["']emoticon[ ].+?["'][>]

下一点是我开始在 phpliveregex.com 的帮助下测试我的正则表达式。结果你可以看到here。只需点击 preg_replace 选项卡。

现在我很确定这对我有用,我试了一下。我用 PHP 写了一个函数:

private function countCharactersOfSpecialUserInput($userInput) {
    $wholeCharacters = 0;
    $input_lines = 'This is a test
                    for<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">my
                    <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">regex 
                    which<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">should
                    be alright <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking">and<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking">
                    match all this emoticons except things like <img dsopjfdojp
                    <img oew> because this ones are not real emoticons! The following is a real one: <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">
                    ';      
    return preg_replace("/<img[ ]src=[\"'].+?[\"'][ ]class=[\"']emoticon[ ].+?[\"'][>]/", "1", $input_lines);
}

在我的函数中,我现在不计算字符数,因为有一个我不明白的错误。这听起来不可能,但它是真实的:-(。

如果我使用保存在变量 $input_lines 中的字符串,它工作得很好。但是,如果我使用用户可以传输的文本,它就不起作用!

我使用 var_dump 和 print_r 从用户那里获取传输的数据。之后我完全使用了这个字符串并将其保存在 input_lines 变量中。令人难以置信的事实是,通过使用 input_lines 变量,它又可以工作了......不管我做什么,我的代码都不会替换单个表情符号,而文本是由用户动态传输的......

您有什么可以想象的可能会导致此问题的情况吗? 我一无所知,我无法相信这是真的。它必须起作用,我尝试了很多其他的方法,但对我没有任何作用......

【问题讨论】:

  • 你不是最好strlen原始源数据(包含表情符号的代码),而不是渲染数据(包含img元素)吗?
  • 我不知道你是否理解我的问题......如果我只使用 strlen 比我只得到一个大约 80 或 90 个字符的表情符号,但用户只使用了 1 个应该算作 1 的表情符号用过的字符!
  • @hek2mgl 如果没有人可以帮助我,我将不得不重新考虑,然后我将看看 php 的 DOM 功能,但我真的更喜欢只用一个正则表达式来解决这个问题......它必须工作,但它没有 - 对于任何建议,我将非常感激。也许你描述了一个使用 DOM @hek2mgl 的解决方案?
  • @user3714751 我已使用DOMDocument 添加了答案。

标签: php regex preg-replace


【解决方案1】:

带有图像的文本实际上是一个 HTML sn-p,因此我会使用 DOM 来解析它:

$input_lines = 'This is a test for<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">my <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">regex which<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">should be alright <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking">and<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking"> match all this emoticons except things like <img dsopjfdojp <img oew> because this ones are not real emoticons! The following is a real one: <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">';

$doc = new DOMDocument();

// Suppress warnings
@$doc->loadHTML($input_lines);

$imgs = $doc->getElementsByTagName("img");
$number_of_imgs = $imgs->length;
echo "Found $number_of_imgs images" . PHP_EOL;

// The plain text is actually the nodeValue of
// the whole snippet.
$text = $imgs->item(0)->parentNode->nodeValue;
$len = mb_strlen($text);

echo "Text length: $len + $number_of_imgs(images)" . PHP_EOL;

看到它的工作:http://3v4l.org/MH5T6

【讨论】:

  • 这不会完美,因为例如 被识别为图像,但它不是真正的表情...。我看不到 domDocument 的解决方案与一个好的正则表达式一样有效。还是我错了?
  • &lt;img dsopjfdojp &lt;img oew&gt; 是什么意思?我很困惑。基本上我会建议发送纯文本并在客户端站点上替换表情符号(或在您使用 PHP 输出内容之前)。数据库不应包含图像标签
  • 好吧,我认为您的意思是在 sql 注入的情况下与数据库有关,但为此您应该知道 user_input 通过一个特殊函数运行,该函数将找到任何具有独特短语的表情符号,不会受到伤害虽然保护消息免受 sql 注入并且毕竟是安全的,但唯一的短语将被解析回图像标签。我提到了 因为用户可以使用它来编写更多文本,因为它只是文本,不会被解析为表情符号,但以你的方式,它会被视为一个字符,你理解我的问题吗?
  • 是的,明白了。请注意,我没有谈论 SQL 注入,我谈论的是功能。表情符号的替换应该发生在视图级别而不是数据库中。这样做,你所有的问题都消失了。这个优势够吗?以 stackoverflow 为例,他们将标记保存在数据库中,而不是呈现的 html。
  • 我想到了这一点,因为我对我网站上的所有区域都这样做了,但我的解决方案的优点是我不需要每个笑脸像 8) 或 :-) 这样的关键字。下一点是 contenteditable 用户已经拥有 1:1 的笑脸视觉效果,就像他发布文本时的样子。关键是我用这种方法做了很多太安全的事情并做到了这一点。如果我改变这一点,就知道所有时间都浪费了。有趣的是,我遇到的唯一问题是我现在无法计算字符......不过谢谢你的意见,我会考虑的。
【解决方案2】:

您最好将表情符号作为文本存储在数据库中。例如,一张快乐的脸可以存储为 :) 或 =) 并且只使用数据库中的 2 个字符。

然后在输出时执行与您在此处所做的操作相反的操作,并使用 preg_replace 将所有 :) 或 =) 等实例替换为相关的 &lt;img src=...

这几乎是所有 Web 应用程序中使用的标准。它将允许您动态更改以后使用的表情符号,例如,如果您更改模板并希望表情符号也发生变化,则您更改表情符号功能,数据库中所有先前出现的事件也会发生变化。

这不仅可以帮助您计算字符数,还可以帮助您将来管理和清理数据库。

<?php
    $input = 'Hello There! :) How are you today?';
    $happy = '<img src="img/smile.gif" border="0" />';

    $output = preg_replace("(\:\))", $happy, $input);

    echo $output;
?>

View In Action

显然,您甚至可以将其调整为使用数据库来管理您的表情符号并使用数组来运行 pregreplace。天空成为极限。

【讨论】:

  • 您知道':)' 不是有效的正则表达式,对吧?你甚至没有分隔符
  • 不知道为什么我把它贴出来了,忘了正确地转义它。道歉并感谢您指出这一点。糟糕透了。
  • 我是否替换 :-) 或图像标签(也只是我可以与正则表达式匹配的文本)在我看来并不重要。所以这并不能帮助我解决问题。
  • 一种方式可行且简单且可扩展,另一种方式目前不可行且复杂?如果您使用 while 或 foreach 循环进行匹配,您也可以从每次出现的长度中 -1。只需在开始时取长度,运行匹配算法,每次出现 :) -1 从长度开始。您的数据变得更易于管理,这对您来说应该很容易实现。
【解决方案3】:

为什么要使用var_dumpprint_r 从用户那里获取数据?那些函数echo 输入到标准输出,它们实际上并不返回字符串。看看:

php > $num_finds = preg_replace("/<img[ ]src=[\"'].+?[\"'][ ]class=[\"']emoticon[ ].+?[\"'][>]/", "1", $lines);
php > echo($num_finds);
1my1regex which1should be alright 1and1 match all this emoticons except things like <img dsopjfdojp <img oew> because this ones are not real emoticons! The following is a real one: 1

工作正常。但是,如果您尝试使用 var_dump,则会得到以下信息:

php > $dump_num_finds = preg_replace("/<img[ ]src=[\"'].+?[\"'][ ]class=[\"']emoticon[ ].+?[\"'][>]/", "1", var_dump($lines));
string(718) "<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">my<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">regex which<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">should be alright <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking">and<img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Not-Talking"> match all this emoticons except things like <img dsopjfdojp <img oew> because this ones are not real emoticons! The following is a real one: <img src="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=" class="emoticon Girl">"
php > echo $dump_num_finds;

同样,原因是var_dump 没有返回任何内容。除非您使用ob_start()ob_get_clean() 之类的东西来使字符串回显到标准输出(imo 是一个糟糕的解决方案并且不起作用),否则您的方法将不起作用。您也可以将 true 作为第二个参数传递给 print_r 以使其返回输出,但我无法理解为什么您首先要使用这些函数中的任何一个。

附:作为旁注,在我看来,您的正则表达式有点草率。您应该使用\s 来表示空白字符而不是[ ]。您也可以只使用不带括号的,它会做同样的事情。此外,您不需要最后一个 &gt; 周围的括号:

<img\ssrc=["'].+?["']\sclass=["']emoticon\s.+?["']>

【讨论】:

  • 抱歉,您理解有误。我不使用 var_dump 或 print_r 从用户那里获取数据我也使用这个函数来调试/测试我的函数返回值只是出于开发原因...... echo 给我的不是数据类型等等。您改进我的正则表达式的提示对此很好,但是 \s 或 [ ] 之间是否真的存在可识别的差异(性能或类似的东西)?
猜你喜欢
  • 2018-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 2020-04-30
相关资源
最近更新 更多