Вопрос по utf-8, unicode, php, pcre – preg_match и UTF-8 в PHP

31

m пытается найти строку в кодировке UTF8, используяpreg_match.

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

Это должно вывести 1, так как "H" находится в индексе 1 в строке "¡Hola!», Но это печатает 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

Есть идеи?

увидетьstackoverflow.com/questions/2187615/... Artefacto

Ваш Ответ

7   ответов
1

что вам нужно, это найти многобайтовую безопасную позицию 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
Это был просто упрощенный пример, но это может быть полезно для других. JW.
18

http://bugs.php.net/bug.php?id=37391'u»

 Переключатель имеет смысл только для pcre, сам PHP не знает об этом.

Из PHP 'С точки зрения, строки являются байтовыми последовательностями, и возвращение байтового смещения кажется логичным (я несказать "правильный").

php знает о модификаторе u это 'см. в руководстве см.ты (PCRE_UTF8) " php.net/manual/en/reference.pcre.pattern.modifiers.php Walt Sorensen
Отлично ... и они неt предоставить mb_preg_replace. JW.
Знай, что то же самоеправила» в отношении обработки utf-8 относится к 5-му параметру$offset, Образец:var_dump(preg_match('/#/u', "\xc3\xa4#",$matches,0,2)); AthanasiusKirchner
эта работа очень хорошо / [^ \ w] / IU Maxim Colesnic
1

возвращаемых 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

34

 Модификатор позволяет интерпретировать как шаблон, так и тему как 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]));
Модификатор u предназначен только для того, чтобы интерпретировать шаблон как UTF-8, а не как субъект. " Это неправда. Сравните, например,preg_split('//', .) сpreg_split('//u', .), С этогох интерпретируется как UTF-8 " немного расплывчато, видитеэтот для фактических эффектов режима Unicode. Artefacto
Забавный факт, это производит различный вывод в зависимости от вашей версии.3v4l.org/iHl4a bishop
@LukaRamishvili Некоторые считают, что это отстой во многих вещах. Michael Robinson
у меня былоНЕТ ПРОБЛЕМЫ с UTF-8 в PHP, так как я начал конвертировать все свои старые сайты в Unicode 4-5 лет назад. TheStoryCoder
25

Попробуйте добавить это(* UTF8) перед регулярным выражением:

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

Магия, благодаря комментарию вhttp://www.php.net/manual/es/function.preg-match.php#95828

Я считаю, что это ответ, работал для меня. работал достаточно. Pooya
это также не работает в моей системе qdinar
Не меняет смещения для меня, но это 'Это интересно знать. Оригинальная документация для этого "особенность" является:pcre.org/pcre.txt BurninLeo
Безразлично»у меня не работает ни PHP 5.6, ни PHP 7 в Ubuntu 16.04.(*UTF8) до разделителя ошибка, после не имеет никакого эффекта. Я подозреваю, что это зависит от того, как / где вы получили свой PHP, в частности, настройки, которыеlibpcre* был скомпилирован с. user2609094
0

Вы можете посмотреть наТ-Regx библиотека.

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

это$match->offset() UTF-8 безопасное смещение.

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));

Вывод моего примера:

    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)
      }
    }
Хорошо, что'почему вы должны включить объяснение того, что делает ваш код. Люди нене понимаю, что вы пытаетесь сделать здесь. nhahtdh
Отредактируйте мой ответ, добавили тесты. Guy Fawkes
Можете ли вы объяснить, что делает ваш код, вместо того, чтобы просто вставлять дамп кода? И как это отвечает на вопрос? nhahtdh
Он делает именно то, что описано в комментариях, и возвращает ПРАВИЛЬНЫЕ смещения строк. Это предмет вопроса. Понятия не имею, почему у меня было -2 для моего ответа. Это работает для меня. Guy Fawkes

Похожие вопросы