2013-10-25 4 views
0

Итак, я столкнулся с этой проблемой, и я упростил ее как можно больше.PHP mb_strlen со строкой append chr (241)

$test = 'XXX' . chr(241) . 'XXX'; 
print($test); // XXX�XXX 
print(mb_strlen($test, 'UTF-8')); // 4 
print(count(str_split($test))); // 7 

Так в основном мой вопрос: почему CHR (241) не возвращает один единственный символ, делая длину строки 7? Это шесть символов, я добавляю один, и это четыре персонажа? Почему chr (241) не равно html-объекту 241?

Прочая информация, указанная ниже. Обратите внимание, что до тех пор, пока вы не добавить X ПОСЛЕ Chr (241), все довольны:

print(mb_detect_encoding($test)); // UTF-8 
print(mb_strlen('XX' . chr(241) . 'XX', 'UTF-8')); // 3 
print(mb_strlen('X' . chr(241) . 'X', 'UTF-8')); // 2 
print(mb_strlen('' . chr(241) . 'X', 'UTF-8')); // 1 
print(mb_strlen('X' . chr(241) . '', 'UTF-8')); // 2 
print(mb_strlen('XXX' . chr(241) . '', 'UTF-8')); // 4 
print(mb_strlen(chr(241), 'UTF-8')); // 1 

Похоже, вопрос кодирования, но как? Файл сохраняется как UTF-8, внутренняя кодировка - UTF-8, и я не передаю данные в любом месте, чтобы испортить их.

ответ

3

В 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

+0

Я не конечно, если это объяснение сложное, или просто «это недопустимо UTF-8». В любом случае, приятное объяснение. – deceze

+0

Спасибо. Конечно, «это недопустимый UTF-8» является верным ответом и, возможно, правильным коротким. Однако это не тот, который объясняет наблюдаемые факты и, следовательно, тот, который имеет самую образовательную ценность. Из ответа вы можете вывести a), что если добавленный символ не был chr (241), а chr (195), то сообщенная длина была бы 6 вместо 4 и b) что mb_strlen() будет анализировать неправильный UTF-8 как правильный учитывая только ведущие байты для руководства (мне нужно будет проверить, чтобы реализация была на 100% уверенной в этом). –

+0

Спасибо за этот ответ. Я предполагаю, что если мой файл был закодирован в ASCII, это дало бы мне ожидаемый ответ (поскольку мое исправление должно было делать mb_strlen ($ test, «ASCII»)). Но какая часть кода является неправильной? Это функция chr()? В документации указано _Отбрасывает односимвольную строку, содержащую символ, указанный ascii._ Похоже, что слово «символ» неточно. – Andrew