В UTF-8 все символы ASCII до 127
представлены одним байтом (двоичное представление 0xxxxxxx
), а кодовые точки больше 127
представляются многобайтными< /strong> последовательности. Многобайтовые последовательности состоят из начального байта и одного или нескольких байтов продолжения.
Биты старшего порядка начального байта указывают нам, сколько байтов продолжения использовать, и для этой цели он имеет две или более старших единиц, за которыми следует 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()
одного и того же символа в латинице-1 и UTF-8.
$euroSignAscii = chr(0x80); // Latin-1 extended ASCII
$euroSignUtf8 = chr(0xe2) . chr(0x82) . chr(0xac); // UTF-8
Обратите внимание, если вы повторяете приведенные выше строки, кодировка вашей консоли или веб-страницы (если это latin-1, то $euroSignAscii
будет выводиться правильно, если это UTF-8, то $euroSignUtf8
будет выводиться правильно).
Ссылки:
Хорошей ссылкой является соответствующая статья UTF-8 в Википедии.
Классический пост Джоэла Спольски Абсолютный минимум, который каждый разработчик программного обеспечения обязательно должен знать о Unicode и наборах символов ( Никаких оправданий!)
И чтобы почувствовать таблицу кодировки UTF-8 и символы Unicode
person
Ioannis Lalopoulos
schedule
25.10.2013