Я создаю API для мобильного приложения, и у меня, похоже, есть проблема с подсчетом длины строки, содержащей смайлики. Мой код:
$str = "✌️ @mention";
printf("strlen: %d" . PHP_EOL, strlen($str));
printf("mb_strlen UTF-8: %d" . PHP_EOL, mb_strlen($str, "UTF-8"));
printf("mb_strlen UTF-16: %d" . PHP_EOL, mb_strlen($str, "UTF-16"));
printf("iconv UTF-16: %d" . PHP_EOL, iconv_strlen(iconv("UTF-8", "UTF-16", $str)));
printf("iconv UTF-16: %d" . PHP_EOL, iconv_strlen(iconv("ISO-8859-1", "UTF-16", $str)));
ответ этого:
strlen: 27
mb_strlen UTF-8: 14
mb_strlen UTF-16: 13
iconv UTF-16: 14
iconv UTF-16: 27
Однако я должен получить 17 в результате. Мы попытались определить длину строки на iOS, Android и Windows Phone, это 17 везде. Фрагмент iOS (swift):
var str = "✌️ @mention"(str as NSString).length // 17
count(str) // 13
count(str.utf16) // 17
count(str.utf8) // 27
Нам нужно использовать NSString из-за библиотеки. Мне нужно это, чтобы получить начальную и конечную позицию «@mention». Если строка содержит только текст или только смайлики, она работает нормально, поэтому, возможно, возникла проблема со смешанным содержимым.
Что я делаю неправильно? Какую еще информацию я могу предоставить вам, ребята, чтобы направить меня в правильном направлении?
Спасибо!
Все ваши функции учитывают разные вещи.
Graphemes: ✌ ️ @ m e n t i o n 13
----------- ----------- -------- --------------------- ------ ------ ------ ------ ------ ------ ------ ------ ------
Code points: U+1F44D U+1F3FF U+270C U+1F3FF U+FE0F U+0020 U+0040 U+006D U+0065 U+006E U+0074 U+0069 U+006F U+006E 14
UTF-16 code units: D83D DC4D D83C DFFF 270C D83C DFFF FE0F 0020 0040 006D 0065 006E 0074 0069 006F 006E 17
UTF-16-encoded bytes: 3D D8 4D DC 3C D8 FF DF 0C 27 3C D8 FF DF 0F FE 20 00 40 00 6D 00 65 00 6E 00 74 00 69 00 6F 00 6E 00 34
UTF-8-encoded bytes: F0 9F 91 8D F0 9F 8F BF E2 9C 8C F0 9F 8F BF EF B8 8F 20 40 6D 65 6E 74 69 6F 6E 27
Строки PHP являются байтами.
strlen()
считает количество байтов в строке: 27.
mb_strlen(..., 'utf-8')
подсчитывает количество кодовых точек (символов Unicode) в строке, когда ее байты декодируются в символы с использованием кодировки UTF-8: 14.
(Другой пример подсчета в значительной степени не имеет смысла, поскольку он основан на обработке входной строки как одной кодировки, когда фактически она содержит данные в другой кодировке.)
NSStrings изначально считаются как кодовые единицы UTF-16. Их 17, а не 14, потому что приведенная выше строка содержит такие символы, как которые не помещаются в одну кодовую единицу UTF-16, поэтому должны быть закодированы как суррогатная пара. Нет никаких функций, которые будут подсчитывать строки в кодовых единицах UTF-16 в PHP, но поскольку каждая кодовая единица кодируется в два байта, вы можете решить это достаточно легко, кодировав в UTF-16 и разделив число байтов на два:
strlen(iconv('utf-8', 'utf-16le', $str)) / 2
(Обратите внимание le
необходимо сделать суффикс iconv
закодировать с определенным порядком байтов UTF-16 и не сбивать счет, выбрав одну и добавив спецификацию в начало строки, чтобы сказать, какую из них он выбрал.)
Я включил картинку, чтобы проиллюстрировать ответ, который дал @bobince.
По сути, все кодовые точки несуррогатной пары заканчиваются как два байта в UTF-16, в то время как все кодовые точки суррогатной пары заканчиваются четырьмя байтами. Если мы разделим это на два, мы получим эквивалентное ожидаемое значение длины.
Постскриптум Пожалуйста, прости ошибку в изображении, где написано «кодовые точки» и должно быть написано «кодовые единицы»