代码之家  ›  专栏  ›  技术社区  ›  JW.

preg_match和php中的utf-8

  •  31
  • JW.  · 技术社区  · 15 年前

    我正在尝试使用 preg_match .

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

    这应该打印1,因为“h”在字符串“hola!”的索引1处。但它印的是2。所以它似乎并没有把主题当作一个utf8编码的字符串,即使我正在传递“u” modifier 在正则表达式中。

    我在php.in i中有以下设置,其他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
    

    有什么想法吗?

    7 回复  |  直到 6 年前
        1
  •  18
  •   Tomalak    9 年前

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

    “U”开关只对PCRE有意义,PHP本身并不知道。

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

        2
  •  34
  •   Mark Amery Harley Holcombe    10 年前

    虽然 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]));
    
        3
  •  25
  •   Natxet    13 年前

    尝试添加这个 (*UTF8) 在regex之前:

    preg_match('(*UTF8)/H/u', "\xC2\xA1Hola!", $a_matches, PREG_OFFSET_CAPTURE);
    

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

        4
  •  5
  •   Artie Groove Guy Fawkes    9 年前

    请原谅我做了尸检,但可能有人会发现它很有用:下面的代码既可以代替preg-match,也可以代替preg-match,返回正确的匹配 对的 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));
    

    我的示例输出:

        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)
          }
        }
    
        5
  •  1
  •   velcrow    13 年前

    如果您只想找到h的多字节安全位置,请尝试mb_strpos()。

    mb_internal_encoding('UTF-8');
    $str = "\xC2\xA1Hola!";
    $pos = mb_strpos($str, 'H');
    echo $str."\n";
    echo $pos."\n";
    echo mb_substr($str,$pos,1)."\n";
    

    输出:

    ¡Hola!
    1
    H
    
        6
  •  1
  •   bronek89    7 年前

    我编写了一个小类来将preg_match返回的偏移量转换为适当的utf偏移量:

    final class NonUtfToUtfOffset
    {
        /** @var int[] */
        private $utfMap = [];
    
        public function __construct(string $content)
        {
            $contentLength = mb_strlen($content);
    
            for ($offset = 0; $offset < $contentLength; $offset ++) {
                $char = mb_substr($content, $offset, 1);
                $nonUtfLength = strlen($char);
    
                for ($charOffset = 0; $charOffset < $nonUtfLength; $charOffset ++) {
                    $this->utfMap[] = $offset;
                }
            }
        }
    
        public function convertOffset(int $nonUtfOffset): int
        {
            return $this->utfMap[$nonUtfOffset];
        }
    }
    

    你可以这样使用它:

    $content = 'aą bać d';
    $offsetConverter = new NonUtfToUtfOffset($content);
    
    preg_match_all('#(bać)#ui', $content, $m, PREG_OFFSET_CAPTURE);
    
    foreach ($m[1] as [$word, $offset]) {
        echo "bad: " . mb_substr($content, $offset, mb_strlen($word))."\n";
        echo "good: " . mb_substr($content, $offsetConverter->convertOffset($offset), mb_strlen($word))."\n";
    }
    

    https://3v4l.org/8Y32J

        7
  •  0
  •   Danon TylerDurden    6 年前

    你可能想看看 T-Regx 图书馆。

    pattern('/Hola/u')->match('\xC2\xA1Hola!')->first(function (Match $match) 
    {
        echo $match->offset();     // characters
        echo $match->byteOffset(); // bytes
    });
    

    这个 $match->offset() 是UTF-8安全偏移。