В UTF-8 все символы ASCII под 127
представлены в виде одного байта (двоичное представление 0xxxxxxx
) и кодовых точек больше, чем 127
представлены многобайтных последовательностей. Многобайтовые последовательности состоят из начального байта и одного или более байт продолжения.
старшего байта в высокого порядка биты служат, чтобы сказать нам, сколько продолжение байтов использовать и для этой цели он имеет два или более 1s высокого порядка, за которыми следует 0, т.е. старшие биты могут быть 110
или 1110
или 11110
или 111110
. Количество битов высокого порядка равны сумме ведущего байта плюс продолжение байт, то есть
110 means 1 leading byte + 1 continuation byte
1110 means 1 leading byte + 2 continuation bytes
11110 means 1 leading byte + 3 continuation bytes
Продолжение байтов, которые следуют ведущие байт имеют формат 10xxxxxx
.
Применяя выше вашей $test
строки:
У нас есть три байта ord('X')
, что все являются ASCII символы под 127
, так что те, считаются 1 символ до 1 байт,
Тогда мы имеем chr(241)
с двоичным представлением 11110001, так что это ведущий байт, поскольку он имеет два или более высоких бит.
Так как он имеет 4 старших бит, что означает, что точка кода представляет состоит из 1 ведущего байта плюс 3 продолжения байт, так что 3 ord('X')
байты, которые остаются в строке рассматриваются mb_strlen()
, как продолжение байт * и хотя вместе с chr (241) составляют в общей сложности четыре байта, они считаются одной кодовой точкой UTF-8.
* Здесь мы должны указать, что те хвосты «X» не являются допустимыми байтами продолжения, поскольку они не соответствуют стандарту байта продолжения. Однако mb_strlen()
будет потреблять, как объяснялось выше, до 3 байтов после chr(241)
. Вы можете проверить это, если вы добавите еще один 'X
'или вы вычтите 'X's
с конца строки $test
.
ОБНОВЛЕНИЕ: Проверка выводы:
/*
* The following strings are non valid UTF-8 encodings.
* We test to see if mb_strlen() consumes non VALID UTF-8
* byte strings like they are valid (driven by the leading bytes)
*
*/
/*
* 0xc0 as a leading byte should consume one continuation byte
* so the length reported should be 6
*/
$test = 'XXX' . chr(0xc0) . 'XXX';
echo '6 == ', mb_strlen($test, 'UTF8');
/*
* 0xe0 as a leading byte should consume two continuation bytes
* so the length reported should be 5
*/
$test = 'XXX' . chr(0xe0) . 'XXX';
echo '5 == ', mb_strlen($test, 'UTF8'), PHP_EOL;
// results in 6 == 6 and 5 == 5
ОБНОВЛЕНИЕ 2:
Пример построения с chr()
один и тот же символ в Latin-1 и UTF-8.
$euroSignAscii = chr(0x80); // Latin-1 extended ASCII
$euroSignUtf8 = chr(0xe2) . chr(0x82) . chr(0xac); // UTF-8
Примите к сведению, если эхо выше строки кодировку вашей консоли или веб-страницы (если это латино-1, то выход $euroSignAscii
будет правильно, если это UTF-8, то выход $euroSignUtf8
будет правильно) ,
Ссылки:
Хорошая ссылка является соответствующая UTF-8 article on Wikipedia
Классический пост от Джоэл Спольски The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
И чтобы почувствовать UTF-8 encoding table and Unicode characters
Я не конечно, если это объяснение сложное, или просто «это недопустимо UTF-8». В любом случае, приятное объяснение. – deceze
Спасибо. Конечно, «это недопустимый UTF-8» является верным ответом и, возможно, правильным коротким. Однако это не тот, который объясняет наблюдаемые факты и, следовательно, тот, который имеет самую образовательную ценность. Из ответа вы можете вывести a), что если добавленный символ не был chr (241), а chr (195), то сообщенная длина была бы 6 вместо 4 и b) что mb_strlen() будет анализировать неправильный UTF-8 как правильный учитывая только ведущие байты для руководства (мне нужно будет проверить, чтобы реализация была на 100% уверенной в этом). –
Спасибо за этот ответ. Я предполагаю, что если мой файл был закодирован в ASCII, это дало бы мне ожидаемый ответ (поскольку мое исправление должно было делать mb_strlen ($ test, «ASCII»)). Но какая часть кода является неправильной? Это функция chr()? В документации указано _Отбрасывает односимвольную строку, содержащую символ, указанный ascii._ Похоже, что слово «символ» неточно. – Andrew