Неверный вывод при использовании индексирования массива в строке UTF-8

Я столкнулся с проблемой при использовании строки UTF-8. Я хочу прочитать один символ из строки, например:

$string = "üÜöÖäÄ";
echo $string[0];

Я ожидаю увидеть ü, но я получаю - почему?

Ответы

Ответ 1

Используйте mb_substr($string, 0, 1, 'utf-8'), чтобы получить символ.

Что происходит в вашем коде, так это то, что выражение $string[0] получает первый байт кодированного представления UTF-8 вашей строки, потому что строки PHP являются эффективными массивами байтов (PHP не распознает внутреннее кодирование).

Поскольку первый символ в вашей строке состоит из более чем одного байта (правила кодирования UTF-8), вы фактически получаете только часть персонажа. Кроме того, эти правила делают байт, который вы получаете недействительным, чтобы стоять как символ самостоятельно, поэтому вы видите знак вопроса.

mb_substr знает правила кодирования, поэтому он не будет наивно возвращать вам только один байт; он получит столько, сколько необходимо для кодирования первого символа.

Вы можете видеть, что $string[0] возвращает только один байт с помощью:

$string = "üÜöÖäÄ";
echo strlen($string[0]);

Пока mb_substr возвращает два байта:

$string = "üÜöÖäÄ";
echo strlen(mb_substr($string, 0, 1, 'utf-8'));

И эти два байта на самом деле всего один символ (для этого вам нужно использовать mb_strlen):

$string = "üÜöÖäÄ";
echo mb_strlen(mb_substr($string, 0, 1, 'utf-8'), 'utf-8');

Наконец, как указывает Marwelln, ситуация становится более терпимой, если вы используете mb_internal_encoding, чтобы избавиться от избыточности 'utf-8':

$string = "üÜöÖäÄ";
mb_internal_encoding('utf-8');
echo mb_strlen(mb_substr($string, 0, 1));

Вы можете видеть больше всего в действии.