我正在为移动应用程序构建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手机上使用字符串长度,它到处都是17。 iOS(swift)片段:
var str = "✌️ @mention"
(str as NSString).length // 17
count(str) // 13
count(str.utf16) // 17
count(str.utf8) // 27
由于库,我们需要使用NSString。我需要这个来获得“@mention”的起始位置和结束位置。如果字符串仅包含文本或仅包含表情符号,则它可以正常工作,因此混合内容可能存在一些问题。
我做错了什么?还有什么其他信息可以让你们让我们朝着正确的方向前进?
谢谢!
答案 0 :(得分:14)
你的功能都在计算不同的东西。
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')
计算字符串中使用UTF-8编码将其字节解码为字符时的代码点数(Unicode字符):14。
(其他示例计数在很大程度上没有意义,因为它们基于将输入字符串视为一种编码,而实际上它包含不同编码的数据。)
NSStrings本身被视为UTF-16代码单元。有17个而不是14个,因为上面的字符串包含之类的字符,它们不适合单个UTF-16代码单元,因此必须编码为代理项对。没有任何函数会在PHP中以UTF-16代码单位对字符串进行计数,但由于每个代码单元都编码为两个字节,因此您可以通过编码为UTF-16并将数量除以字节数加两:
strlen(iconv('utf-8', 'utf-16le', $str)) / 2
(注意:le
后缀必须使iconv
编码为UTF-16的特定字节序,而不是通过选择一个并在BOM的开头添加BOM来计算计数。字符串,说明它选择了哪一个。)
答案 1 :(得分:4)
我附上了一张图片,以帮助说明@bobince给出的答案。
基本上,所有非代理对代码点最终都是UTF-16中的两个字节,而所有代理对代码点最终都是四个字节。如果我们将其除以2,我们得到等效的预期长度值。
P.S。请原谅图像中出现“代码点”的错误,并应说“代码单位”
答案 2 :(得分:1)
我知道这是一个老问题,但我最近遇到了这个问题,这可能对其他人也有帮助。
问题中的字符串:
$str = "??✌?️ @mention";
我希望它数为 11:??✌?️
{2}、space
{1}、@
{1}、mention
{7}
所以2 + 1 + 1 + 7 = 11
PHP 的 intl
扩展有一个 grapheme_strlen()
函数,它以字素为单位获取字符串长度,而不是字节或字符 (documentation)
<?php
$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)));
printf("grapheme_strlen: %d" . PHP_EOL, grapheme_strlen($str));
PHP 7.4 上的输出:
strlen: 27
mb_strlen UTF-8: 14
mb_strlen UTF-16: 13
PHP Notice: iconv_strlen(): Detected an illegal character in input string in php shell code on line 1
iconv UTF-16: 0
PHP Notice: iconv_strlen(): Detected an illegal character in input string in php shell code on line 1
iconv UTF-16: 0
grapheme_strlen: 11