function seems_utf8($str) { $length = strlen($str); for ($i=0; $i < $length; $i++) { $c = ord($str[$i]); if ($c < 0x80) $n = 0; # 0bbbbbbb elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b else return false; # Does not match any model for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) return false; } } return true; }
Я получил этот код из WordPress, я мало знаю об этом, но я хотел бы знать, что именно происходит в этой функции.
Если кто-нибудь знает, пожалуйста, помогите мне?
Мне нужно четкое представление о вышеуказанном коде. Если объяснение по строкам будет более полезным.
Я использую два способа проверить, является ли строка utf-8 (в зависимости от случая):
mb_internal_encoding('UTF-8'); // always needed before mb_ functions, check note below if (mb_strlen($string) != strlen($string)) { /// not single byte }
— ИЛИ —
if (preg_match('!\S!u', $string)) { // utf8 }
Для mb_internal_encoding – из-за некоторой неизвестной мне ошибки в php (версия 5.3- (не протестировала ее на 5.3)) передача кодировки в качестве параметра функции mb_ не работает, и внутренняя кодировка должна быть установлена до любое использование функций mb_.
Этот алгоритм в основном проверяет, соответствует ли последовательность байтов шаблону, который вы можете видеть в статье Википедии .
Цикл for
должен пройти через все байты в $str
. ord
получает десятичное число текущего байта. Затем это число проверяется на некоторые свойства.
Если число, если меньше 128 (0x80), это однобайтовый символ. Если он равен или больше 128, проверяется длина многобайтового символа. Это может быть сделано с первым символом многобайтовой последовательности символов. Если первый байт начинается с 110xxxxx
, это двухбайтовый символ; 1110xxxx
, это трехбайтовый символ и т. Д.
Я думаю, что наиболее загадочными частями являются выражения типа ($c & 0xE0) == 0xC0
. То есть проверить, имеет ли число в двоичном формате определенный шаблон. Я попытаюсь объяснить, как это работает на одном примере.
Поскольку все числа, которые мы тестируем для этого шаблона, равны или больше 0x80, первый байт всегда равен 1, поэтому шаблон ограничен как минимум 1xxxxxxxx
. Если мы сделаем 11100000
И сравнение с 11100000
( 11100000
), мы получим этот результат:
1xxxxxxx & 11100000 = 1xx00000
Таким образом, биты в позициях 5 и 6 (считанные справа, индекс, начинающийся с 0), зависят от нашего текущего числа. Чтобы иметь значение равное 11000000
, 5-й бит должен быть 0
а 6-й бит должен быть 1
:
1xxxxxxx & 11100000 ≟ 11000000 ↓↓ → 110xxxxx
Это означает, что остальные биты нашего номера могут быть произвольными: 110xxxxx
. И это именно то, что шаблон в статье Википедии предсказывает для первого байта двухбайтового символьного слова.
И последний внутренний цикл должен проверять здравомыслие следующих байтов многобайтового символа. Все они должны начинаться с 10xxxxxx
.
Если вы немного знаете о UTF-8, это довольно простая реализация.
function seems_utf8($str) { # get length, for utf8 this means bytes and not characters $length = strlen($str); # we need to check each byte in the string for ($i=0; $i < $length; $i++) { # get the byte code 0-255 of the i-th byte $c = ord($str[$i]); # utf8 characters can take 1-6 bytes, how much # exactly is decoded in the first character if # it has a character code >= 128 (highest bit set). # For all <= 127 the ASCII is the same as UTF8. # The number of bytes per character is stored in # the highest bits of the first byte of the UTF8 # character. The bit pattern that must be matched # for the different length are shown as comment. # # So $n will hold the number of additonal characters if ($c < 0x80) $n = 0; # 0bbbbbbb elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b else return false; # Does not match any model # the code now checks the following additional bytes # First in the if checks that the byte is really inside the # string and running over the string end. # The second just check that the highest two bits of all # additonal bytes are always 1 and 0 (hexadecimal 0x80) # which is a requirement for all additional UTF-8 bytes for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) return false; } } return true; }
Кстати. На PHP я предполагаю, что это фактор 50-100 медленнее, чем функция C, поэтому вы не должны использовать ее на длинных строках и производственных системах.
наткнулся на этот пост, имел аналогичную проблему. mb_detect_encoding показал utf-8, но mb_check_encoding вернул false …
исправить, для меня решение было:
$cur_encoding = mb_detect_encoding($in_str) ; if($cur_encoding == "UTF-8" && mb_check_encoding($in_str,"UTF-8")) return $in_str; else return utf8_encode($in_str);
получил его оттуда: http://board.phpbuilder.com/showthread.php?10368156-mb_check_encoding%28-in_str-quot-UTF-8-quot-%29-return-different-results
sry не удалось опубликовать ссылку правильно ….