PHP - 包含表情符号/特殊字符的字符串长度

gab*_*abo 8 php unicode unicode-string

我正在为移动应用程序构建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)));
Run Code Online (Sandbox Code Playgroud)

这个的反应是:

strlen: 27
mb_strlen UTF-8: 14
mb_strlen UTF-16: 13
iconv UTF-16: 14
iconv UTF-16: 27
Run Code Online (Sandbox Code Playgroud)

但是我应该得到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
Run Code Online (Sandbox Code Playgroud)

由于库,我们需要使用NSString.我需要这个来获得"@mention"的起始位置和结束位置.如果字符串仅包含文本或仅包含表情符号,则它可以正常工作,因此混合内容可能存在一些问题.

我究竟做错了什么?还有什么其他信息可以让你们让我们朝着正确的方向前进?

谢谢!

bob*_*nce 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
Run Code Online (Sandbox Code Playgroud)

PHP字符串是本机字节.

strlen() 计算字符串中的字节数:27.

mb_strlen(..., 'utf-8') 当字节使用UTF-8编码解码为字符时,计算字符串中的代码点数(Unicode字符):14.

(其他示例计数在很大程度上没有意义,因为它们基于将输入字符串视为一种编码,而实际上它包含不同编码的数据.)

NSStrings本身被视为UTF-16代码单元.有17而不是14,因为上面的字符串包含 that don't fit in a single UTF-16 code unit, so have to be encoded as a surrogate pair. There aren't any functions that will count strings in UTF-16 code units in PHP, but because each code unit is encoded to two bytes, you can work it out easily enough by encoding to UTF-16 and dividing the number of bytes by two:

strlen(iconv('utf-8', 'utf-16le', $str)) / 2
Run Code Online (Sandbox Code Playgroud)

(Note: the le后缀对于iconv对UTF-16的特定字节序进行编码是必要的,并且不会通过选择一个并在字符串的开头添加BOM以说明它选择了哪一个来计算计数.)


Den*_*ell 5

我附上了一张图片来帮助说明@bobince 给出的答案。

本质上,所有非代理对代码点在 UTF-16 中都是两个字节,而所有代理对代码点都是四个字节。如果我们将其除以二,我们将得到等效的预期长度值。

PS请原谅图像中的错误,它说“代码点”并且应该说“代码单元”

Unicode 分解


小智 5

我知道这是一个较旧的问题,但我最近遇到了这个问题,这也可能对其他人有帮助。

\n

问题中的字符串:

\n
$str = "\xe2\x9c\x8c\xef\xb8\x8f @mention";\n
Run Code Online (Sandbox Code Playgroud)\n

我希望它能数到 11:\xe2\x9c\x8c\xef\xb8\x8f{2}、space{1}、@{1}、mention{7}\nso2 + 1 + 1 + 7 = 11

\n

PHP 的intl扩展有一个grapheme_strlen()函数,可以获取字素单位的字符串长度,而不是字节或字符(文档

\n
$str = "\xe2\x9c\x8c\xef\xb8\x8f @mention";\n
Run Code Online (Sandbox Code Playgroud)\n

PHP 7.4 上的输出:

\n
strlen: 27\nmb_strlen UTF-8: 14\nmb_strlen UTF-16: 13\nPHP Notice:  iconv_strlen(): Detected an illegal character in input string in php shell code on line 1\niconv UTF-16: 0\nPHP Notice:  iconv_strlen(): Detected an illegal character in input string in php shell code on line 1\niconv UTF-16: 0\ngrapheme_strlen: 11\n
Run Code Online (Sandbox Code Playgroud)\n

或者在这里尝试: https: //www.tehplayground.com/s8e40DAslYX4Mozo

\n