PHP中的preg_match和UTF-8

JW.*_*JW. 33 php unicode pcre utf-8

我正在尝试使用preg_match搜索UTF8编码的字符串.

preg_match('/H/u', "\xC2\xA1Hola!", $a_matches, PREG_OFFSET_CAPTURE);
echo $a_matches[0][1];
Run Code Online (Sandbox Code Playgroud)

这应该打印1,因为"H"在字符串"¡Hola!"中的索引1处.但它打印2.所以它似乎并没有将主题视为UTF8编码的字符串,即使我在正则表达式中传递"u" 修饰符.

我在php.ini中有以下设置,其他UTF8函数正在运行:

mbstring.func_overload = 7
mbstring.language = Neutral
mbstring.internal_encoding = UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Gum*_*mbo 39

尽管u修饰符使得模式和主题都被解释为UTF-8,但捕获的偏移量仍以字节为单位计算.

您可以使用mb_strlen以UTF-8字符而不是字节来获取长度:

$str = "\xC2\xA1Hola!";
preg_match('/H/u', $str, $a_matches, PREG_OFFSET_CAPTURE);
echo mb_strlen(substr($str, 0, $a_matches[0][1]));
Run Code Online (Sandbox Code Playgroud)

  • "u修饰符仅用于将模式解释为UTF-8,而不是主题." 这不是真的.比较例如`preg_split('//',.)`和`preg_split('// u',.)`.由于这个"x被解释为UTF-8"有点模糊,请参见[this](http://www.pcre.org/pcre.txt)了解unicode模式的实际效果. (3认同)
  • @Tomalak"Man,它是2019年,PHP仍然非常糟糕地使用Unicode." 请确认. (3认同)
  • 根据http://nl1.php.net/manual/en/reference.pcre.pattern.modifiers.php#103348,*u*修饰符对模式**和输入**都有影响. (2认同)

Nat*_*xet 25

尝试在正则表达式之前添加此(*UTF8):

preg_match('(*UTF8)/H/u', "\xC2\xA1Hola!", $a_matches, PREG_OFFSET_CAPTURE);
Run Code Online (Sandbox Code Playgroud)

魔术,感谢http://www.php.net/manual/es/function.preg-match.php#95828中的评论 .

  • 在Ubuntu 16.04上对PHP 5.6或PHP 7不起作用.`(*UTF8)`在分隔符之前是一个错误,之后没有效果.我怀疑它取决于你如何/在哪里得到你的PHP,特别是`libpcre*`编译的设置. (3认同)
  • 有意思,虽然我认为你需要在`(*UTF8)之前的初始`/`.这不适用于我的系统,但可能在其他系统上.当你做`echo $ a_matches [0] [1];`时输出是什么? (2认同)
  • 我在 PHP 5.4.29 上这样使用它,效果就像一个魅力: `preg_match_all('/(*UTF8)[^A-Za-z0-9\s]/', $txt, $matches);` (2认同)
  • 不会改变我的偏移量,但这是一件有趣的事情。该“功能”的原始文档是:http://www.pcre.org/pcre.txt (2认同)

use*_*291 20

看起来这是一个"功能",请参阅 http://bugs.php.net/bug.php?id=37391

'u'开关只对pcre有意义,PHP本身并不知道它.

从PHP的角度来看,字符串是字节序列,返回字节偏移似乎是合乎逻辑的(我不说"正确").

  • 太棒了...而且他们没有提供mb_preg_replace. (4认同)

Guy*_*kes 5

请原谅我,但可能有人会觉得它很有用:下面的代码既可以作为preg_match和preg_match_all函数的替代,也可以返回UTF8编码字符串的正确偏移的正确匹配.

     mb_internal_encoding('UTF-8');

     /**
     * Returns array of matches in same format as preg_match or preg_match_all
     * @param bool   $matchAll If true, execute preg_match_all, otherwise preg_match
     * @param string $pattern  The pattern to search for, as a string.
     * @param string $subject  The input string.
     * @param int    $offset   The place from which to start the search (in bytes).
     * @return array
     */
    function pregMatchCapture($matchAll, $pattern, $subject, $offset = 0)
    {
        $matchInfo = array();
        $method    = 'preg_match';
        $flag      = PREG_OFFSET_CAPTURE;
        if ($matchAll) {
            $method .= '_all';
        }
        $n = $method($pattern, $subject, $matchInfo, $flag, $offset);
        $result = array();
        if ($n !== 0 && !empty($matchInfo)) {
            if (!$matchAll) {
                $matchInfo = array($matchInfo);
            }
            foreach ($matchInfo as $matches) {
                $positions = array();
                foreach ($matches as $match) {
                    $matchedText   = $match[0];
                    $matchedLength = $match[1];
                    $positions[]   = array(
                        $matchedText,
                        mb_strlen(mb_strcut($subject, 0, $matchedLength))
                    );
                }
                $result[] = $positions;
            }
            if (!$matchAll) {
                $result = $result[0];
            }
        }
        return $result;
    }

    $s1 = '????????? ??????? ?????? ??? ?????';
    $s2 = 'Try english string for test';

    var_dump(pregMatchCapture(true, '/???/', $s1));
    var_dump(pregMatchCapture(false, '/???/', $s1));

    var_dump(pregMatchCapture(true, '/lish/', $s2));
    var_dump(pregMatchCapture(false, '/lish/', $s2));
Run Code Online (Sandbox Code Playgroud)

我的例子输出:

    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(2) {
          [0]=>
          string(6) "???"
          [1]=>
          int(4)
        }
      }
    }
    array(1) {
      [0]=>
      array(2) {
        [0]=>
        string(6) "???"
        [1]=>
        int(4)
      }
    }
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(2) {
          [0]=>
          string(4) "lish"
          [1]=>
          int(7)
        }
      }
    }
    array(1) {
      [0]=>
      array(2) {
        [0]=>
        string(4) "lish"
        [1]=>
        int(7)
      }
    }
Run Code Online (Sandbox Code Playgroud)

  • 你能解释一下代码的作用而不仅仅是粘贴代码转储吗?这是如何回答这个问题的呢? (2认同)
  • 它完全按照注释中的描述执行并返回正确的字符串偏移量。这是问题的主题。不知道为什么我的答案是 -2。它对我有用。 (2认同)