【问题标题】:mb_str_replace()... is slow. any alternatives?mb_str_replace()... 很慢。有什么选择吗?
【发布时间】:2011-03-30 04:53:52
【问题描述】:

我想确保我正在运行的一些字符串替换是多字节安全的。我在网上找到了一些 mb_str_replace 函数,但它们很慢。我说的是通过它可能会增加 500-900 个字节后增加 20%。

有什么建议吗?我正在考虑使用 preg_replace ,因为它是本机的并已编译,因此它可能会更快。任何想法将不胜感激。

【问题讨论】:

  • 您需要提供更多信息。主题的替换字符串和编码是什么?如果主题为 UTF-8 且替换字符串在 ASCII 范围内,则可以使用str_replace
  • Unicode 已经存在了,什么,15 年了?还在核心内循环中使用 mb 字符串?从内到外工作。

标签: php multibyte


【解决方案1】:

正如there 所说,只要所有参数都是 utf-8 有效的,str_replace 在 utf-8 上下文中使用是安全的,因为它不会是两个多字节编码字符串之间的任何模棱两可的匹配。如果您检查输入的有效性,则无需寻找其他功能。

【讨论】:

  • 如果您使用 unicode 并关心 unicode equivalence,这是错误的。在 unicode 中,几个不同的字节序列可以表示同一个字符。使用str_replace只有如果你首先标准化你的两个字符串才有效。
  • 好的提示,无论如何我对“多字节安全”的理解是“他们在匹配时不会给出任何误报”,这实际上意味着他们不会破坏输出信息它是替换所需要的。
  • 检查提供的链接
  • 值得注意的是,UTF-8 是 ASCII 的正确超集,更重要的是,多字节 UTF-8 字符永远不会包含 ASCII 八位字节。因此,如果您的 $search$replace 仅包含 ASCII,您可以安全地在 UTF-8 主题上使用 str_replace()
  • Re:Qtax unicode 规范化,我发现Unicode Normalization Forms 规范比维基百科页面更容易理解。
【解决方案2】:

当有来自任何地方(utf8 或其他)的输入时,编码是一个真正的挑战,我更喜欢只使用多字节安全函数。对于str_replace,我使用的是this one,这已经足够快了。

if (!function_exists('mb_str_replace'))
{
   function mb_str_replace($search, $replace, $subject, &$count = 0)
   {
      if (!is_array($subject))
      {
         $searches = is_array($search) ? array_values($search) : array($search);
         $replacements = is_array($replace) ? array_values($replace) : array($replace);
         $replacements = array_pad($replacements, count($searches), '');
         foreach ($searches as $key => $search)
         {
            $parts = mb_split(preg_quote($search), $subject);
            $count += count($parts) - 1;
            $subject = implode($replacements[$key], $parts);
         }
      }
      else
      {
         foreach ($subject as $key => $value)
         {
            $subject[$key] = mb_str_replace($search, $replace, $value, $count);
         }
      }
      return $subject;
   }
}

【讨论】:

    【解决方案3】:

    这是我的实现,基于Alain's answer

    /**
     * Replace all occurrences of the search string with the replacement string. Multibyte safe.
     *
     * @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
     * @param string|array $replace The replacement value that replaces found search values. An array may be used to designate multiple replacements.
     * @param string|array $subject The string or array being searched and replaced on, otherwise known as the haystack.
     *                              If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.
     * @param string $encoding The encoding parameter is the character encoding. If it is omitted, the internal character encoding value will be used.
     * @param int $count If passed, this will be set to the number of replacements performed.
     * @return array|string
     */
    public static function mbReplace($search, $replace, $subject, $encoding = 'auto', &$count=0) {
        if(!is_array($subject)) {
            $searches = is_array($search) ? array_values($search) : [$search];
            $replacements = is_array($replace) ? array_values($replace) : [$replace];
            $replacements = array_pad($replacements, count($searches), '');
            foreach($searches as $key => $search) {
                $replace = $replacements[$key];
                $search_len = mb_strlen($search, $encoding);
    
                $sb = [];
                while(($offset = mb_strpos($subject, $search, 0, $encoding)) !== false) {
                    $sb[] = mb_substr($subject, 0, $offset, $encoding);
                    $subject = mb_substr($subject, $offset + $search_len, null, $encoding);
                    ++$count;
                }
                $sb[] = $subject;
                $subject = implode($replace, $sb);
            }
        } else {
            foreach($subject as $key => $value) {
                $subject[$key] = self::mbReplace($search, $replace, $value, $encoding, $count);
            }
        }
        return $subject;
    }
    

    他不接受字符编码,虽然我想你可以通过mb_regex_encoding 设置它。

    我的单元测试通过了:

    function testMbReplace() {
        $this->assertSame('bbb',Str::mbReplace('a','b','aaa','auto',$count1));
        $this->assertSame(3,$count1);
        $this->assertSame('ccc',Str::mbReplace(['a','b'],['b','c'],'aaa','auto',$count2));
        $this->assertSame(6,$count2);
        $this->assertSame("\xbf\x5c\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'iso-8859-1'));
        $this->assertSame("\xbf\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'gbk'));
    }
    

    【讨论】:

      【解决方案4】:

      http://php.net/manual/en/ref.mbstring.php#109937 上评分最高的注释说 str_replace 适用于多字节字符串。

      【讨论】:

      • 啊 - 记得检查整个评论,而不仅仅是第一句话。引用:Problems arise if you are getting a value "from outside" somewhere (database, POST request) and the encoding of the needle and the haystack is not the same. 所以只有在“完美世界”类型示例中的特定用例中,str_replace 才适用于多字节字符串。
      猜你喜欢
      • 2021-10-31
      • 1970-01-01
      • 1970-01-01
      • 2013-10-27
      • 2013-12-19
      • 1970-01-01
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多