PHP – длина строки, содержащей emojis / специальные символы

Я создаю API для мобильного приложения, и у меня, похоже, проблема с подсчетом длины строки, содержащей emojis. Мой код:

$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-телефоне, это повсеместно. iOS (быстрый) фрагмент:

 var str = "👍🏿✌🏿️ @mention" (str as NSString).length // 17 count(str) // 13 count(str.utf16) // 17 count(str.utf8) // 27 

Нам нужно использовать NSString из-за библиотеки. Мне нужно это, чтобы получить начальную и конечную позицию «@mention». Если строка содержит только текст или только emojis, она отлично работает, поэтому, возможно, есть проблема со смешанным контентом.

Что я делаю не так? Какую еще информацию я могу предоставить вам, ребята, чтобы помочь мне в правильном направлении?

Благодаря!

В ваших функциях все подсчитываются разные вещи.

 Graphemes: 👍 🏿 ✌ 🏿️ @ mention 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 

(Примечание: суффикс файла необходим, чтобы сделать кодировку iconv определенной контентой UTF-16, а не сгладить счет, выбирая ее и добавляя спецификацию к началу строки, чтобы сказать, какой из них она выбрала.)

Я включил изображение, чтобы помочь проиллюстрировать ответ, который дал @bobince.

По сути, все кодовые точки без суррогатной пары заканчиваются как два байта в UTF-16, тогда как все суррогатные пары кодовых точек заканчиваются как четыре байта. Если мы разделим это на два, мы получим эквивалентное ожидаемое значение длины.

PS Пожалуйста, простите ошибку на изображении, где она говорит «кодовые точки», и должна сказать «единицы кода»,

распад Юникода