Intereting Posts
проблемы в создании расширенного поиска с использованием запроса Использование заголовка в PHP PHP – Как разобрать этот xml? Нужна помощь с введением нового элемента XML для детей с помощью PHP Проверьте, существует ли файл на удаленной машине PHPMailer – SMTP ERROR: команда паролей не удалась при отправке почты с моего сервера session_start Вызывает фатальную ошибку mod_rewrite, чтобы удалить .php, но все равно служить файлу .php? php – Получить сжатое содержимое с помощью cURL Использование X-editable для создания нескольких полей, редактируемых в существующей горизонтальной форме bootstrap3 Получение ошибки Parse: синтаксическая ошибка, неожиданные '}', ожидающие ',' или '; как найти массив появления символа в строке WooCommerce не может получить доступ к тележке из класса продукта Почему эта таблица нескольких строк отправляется по строке? Это верно? Третий четверг месяца в php

preg_match и UTF-8 в PHP

Я пытаюсь найти строку с кодировкой 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 

Есть идеи?

Похоже, что это «функция», см. http://bugs.php.net/bug.php?id=37391

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

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

Хотя модификатор 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])); 

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

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

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

Извините меня за некропостинг, но может быть кто-то сочтет это полезным: код ниже может работать как замена для функций 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) } } 

Я написал небольшой класс для преобразования смещений, возвращаемых 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

Если все, что вы хотите сделать, это найти многобайтовое безопасное положение H try 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