美文网首页
NSString 中 length 计算的是文本长度吗?

NSString 中 length 计算的是文本长度吗?

作者: userName | 来源:发表于2020-03-27 14:17 被阅读0次

    前几天在项目中用NSString的.length 方法发现到一些问题,例如:

        NSString *chineseStr = @"哈";
        NSString *englishStr = @"a";
        NSString *numberStr = @"1";
        NSString *symbolStr = @",";
        NSString *emojiStr = @"🌞";
        NSString *emijiStr2 = @"🏴";
    
        NSLog(@"chineseStr.length=%ld",(long)chineseStr.length);
        NSLog(@"englishStr.length=%ld",(long)englishStr.length);
        NSLog(@"numberStr.length=%ld",(long)numberStr.length);
        NSLog(@"symbolStr.length=%ld",(long)symbolStr.length);
        NSLog(@"emojiStr.length=%ld",(long)emojiStr.length);
        NSLog(@"emijiStr2.length=%ld",(long)emijiStr2.length);
    
    

    我们看下log结果

    2018-08-27 14:48:36.471390+0800 testlegnth[9606:205748] chineseStr.length=1
    2018-08-27 14:48:36.471552+0800 testlegnth[9606:205748] englishStr.length=1
    2018-08-27 14:48:36.471801+0800 testlegnth[9606:205748] numberStr.length=1
    2018-08-27 14:48:36.471993+0800 testlegnth[9606:205748] symbolStr.length=1
    2018-08-27 14:48:36.472230+0800 testlegnth[9606:205748] emojiStr.length=2
    2018-08-27 14:48:36.472230+0800 testlegnth[9606:205748] emijiStr2.length=14
    
    

    除了emoji其他的长度都是1,并且不同的emoji 的长度还不同。那length到底代表什么含义。

    1.官方文档的解释

    NSString文档中这么写的:文档地址

    A string object presents itself as an array of Unicode characters …. You can determine how many characters a string object contains with the length method and can retrieve a specific character with the characterAtIndex: method. These two “primitive” methods provide basic access to a string object.

    大概意思就是

    一个字符串对象代表着一个 Unicode 字符组成的数组…… 可以用 length 方法来获得一个字符串对象所包含的字符数;用 characterAtIndex: 方法取得特定的字符。这两个简单的方法为访问字符串对象提供了基本的途径。

    注:

    • 其实上面已经说的很清楚了,length并不代表着文本的长度,他的含义只是 一个NSString中 Unicode 字符组成的数组的长度。
      接下来说下Unicode是如何组成文字的。

    2.Unicode 概要

    书写系统里所使用的每一个字符或符号定义了一个唯一的数字。这个数字叫做码点(code points),以 U+xxxx 这样的格式写成,格式里的 xxxx 代表四到六个十六进制的数。比如,U+0041(十进制是 65)这个码点代表拉丁字母表(和 ASCII 一致)里的字母 A;U+1F61B 代表名为“伸出舌头的脸”的 emoji,也就是 😛。例如:

    NSString *unicodeStr = @"\u4eca\u5929\u5929\u6c14\u4e0d\u9519";
    NSLog(@"unicodeStr message=%@",unicodeStr);
    
    

    输出

    unicodeStr message=今天天气不错
    
    

    注:

    • 正常情况下一个Unicode代表着1个字符。但是由于 Unicode 的特性有些情况下,多个Unicode才能代表一个字符。

    3.Unicode 的一些特性

    1)组合字符序列

    为了和已有的标准兼容,某些字符可以表示成以下两种形式之一:一个单一的码点,或者两个以上连续的码点组成的序列。例如,有重音符号的字母 é 可以直接表示成 U+00E9(「有尖音符号的小写拉丁字母 e」),或者也可以表示成由 U+0065(「小写拉丁字母 e」)再加 U+0301(「尖音符号」)组成的分解形式。这两个形式都是组合字符序列的变体。

    在 Unicode 的语境下,两种形式并不相等(因为两种形式包含不同的码点),但是符合「标准等价」(canonically equivalent),也就是说,它们有着相同的外观和意义。

    2)重复的字符

    许多看上去一样的字符都在不同的码点编码了多次,以此来代表不同的含义。例如,拉丁字母 A(U+0041)就与西里尔字母 A(U+0410)完全同形,但事实上,它们是不同的。把它们编入不同的码点不仅简化了与老的编码系统的转换,而且能让 Unicode 的文本保留字符的含义。

    但也有极少数真正的重复,这些完全相同的字符在不同的码点上定义了多次。例如,Unicode 联盟就列举出了字母 Å(「上面带个圆圈的大写拉丁字母 A」,U+00C5)和字符 Å(「埃米」(长度单位)符号,U+212B)。考虑到「埃米」符号其实就是被定义成这个瑞典大写字母的,因此这两个字符是完全相同的。在 Unicode 里,它们也符合标准等价但不相等。

    还有更多的字符和序列是更广意义上的「重复」,在 Unicode 标准里叫做「相容等价」(compatibility equivalence)。相容的序列代表着相同的字符,但有着不同的外观和表现。例子包括很多被用作数学和技术符号的希腊字母,还有,尽管已经有了从 U+2160 到 U+2183 这个范围里的标准拉丁字母,罗马数字也被单独编入 Unicode。其它关于相容等价的典型例子就是连字(ligature):字母 ff(小写拉丁连字 ff,U+FB00)和 ff 的序列(小写拉丁字母 f + 小写拉丁字母 f,U+0066 U+0066)就符合相容等价但不符合标准等价,虽然它们也可能以完全一致的样子呈现出来,这取决于环境、字体以及文本的渲染系统。

    3)正规形式

    从上面可以看出,在 Unicode 里,字符串的等价性并不是一个简单的概念。除了一个码点一个码点地比较两个字符串以外,我们还需要另一种方式来鉴定标准等价和相容等价。为此,Unicode 定义了几个正规化(normalization)算法。正规化一个字符串的意思是:为了能使它与另一个正规化了的字符串进行二进制比较(binary-compare),将其转化成有且仅有的唯一一个表示形式,这个形式由等价字符的序列组成。

    4)字形变体

    有些字体会为一个字符提供多个字形(glyph)变体。Unicode 提供了一个叫做「变体序列」(variation sequences)的机制,它允许用户选择其中一个变体。这和组合字符序列的工作机制完全一样:一个基准字符加上 256 个变体选择符(VS1-VS256,U+FE00 到 U+FE0F,还有 U+E0100 到 U+E01EF)中的一个。Unicode 标准对「标准化变体序列」(Standardized Variation Sequences,在 Unicode 标准中定义)和「象形文字变体序列」(Ideographic Variation Sequences,是由第三方提交给 Unicode 联盟的,一旦注册,它可以被任何人使用)做出了区分。技术上来讲,两者并无区别。

    emoji 的样式就是一个标准化变体序列的例子。许多 emoji 和一些「正常」的字符都有两种风格:一种是彩色的「emoji 风格」,另一种是黑白的,更像是符号的「文本风格」。例如,「有雨滴的伞」这个字符(U+2614)可能是这样:☔️ (U+2614 U+FE0F) ,也可能是这样的:☂️ (U+2614 U+FE0E)。

    4.Unicode 转换格式(UTF-32,UTF-16,UTF-8)

    UTF-32

    最清楚明了的一个 UTF 就是 UTF-32:它在每个码点上使用整 32 位。32 大于 21,因此每一个 UTF-32 值都可以直接表示对应的码点。尽管简单,UTF-32却几乎从来不在实际中使用,因为每个字符占用 4 字节太浪费空间了。
    解释下21位的由来,最初,Unicode 编码是被设计为 16 位的,提供了 65,536 个字符的空间。当时人们认为这已经大到足够编码世界上现代文本里所有的文字和字符了。后来,考虑到要编码历史上的文字以及一些很少使用的日本汉字和中国汉字,Unicode 编码扩展到了 21 位(从 U+0000 到 U+10FFFF)。接下来的 NSString 的内容是什么样的,Unicode 不是 16 位的编码!它是 21 位的。这 21 位提供了 1,114,112 个码点,其中,只有大概 10% 正在使用,所以还有相当大的扩充空间。

    UTF-16

    UTF-16 要常见得多,而且在下文我们会看到,它与我们讨论 NSString 对 Unicode 的实现息息相关。它是根据有 16 位固定长度的码元(code units)定义的。UTF-16 本身是一种长度可变的编码。基本多文种平面(BMP)中的每一个码点都直接与一个码元相映射。鉴于 BMP 几乎囊括了所有常见字符,UTF-16 一般只需要 UTF-32 一半的空间。其它平面里很少使用的码点都是用两个 16 位的码元来编码的,这两个合起来表示一个码点的码元就叫做代理对(surrogate pair)。

    为了避免用 UTF-16 编码的字符串里的字节序列产生歧义,以及能使检测代理对更容易,Unicode 标准限制了 U+D800 到 U+DFFF 范围内的码点用于 UTF-16,这个范围内的码点值不能分配给任何字符。当程序在一个 UTF-16 编码的字符串里发现在这个范围内的序列时,就能立刻知道这是某个代理对的一部分。实际的编码算法很简单,维基百科上 UTF-16 的文章里有更多介绍。UTF-16 的这种设计也是为什么码点最长也只有奇怪的 21 位的原因。UTF-16 下,U+10FFFF 是能编码的最高值。

    和所有多字节长度的编码系统一样,UTF-16(以及 UTF-32)还得解决字节顺序的问题。在内存里存储字符串时,大多数实现方式自然都采用自己运行平台的 CPU 的字节序(endianness);而在硬盘里存储或者通过网络传输字符串时,UTF-16 允许在字符串的开头插入一个「字节顺序标记」(Byte Order Mask,简称 BOM)。字节顺序标记是一个值为 U+FEFF 的码元,通过检查文件的头两个字节,解码器就可以识别出其字节顺序。字节顺序标记不是必须的,Unicode 标准把高字节顺序(big-endian byte order)定为默认情况。UTF-16 需要指明字节顺序,这也是为什么 UTF-16 在文件格式和网络传输方面不受欢迎的一个原因,不过微软和苹果都在自己的操作系统内部使用它。

    UTF-8

    由于 Unicode 的前 256 个码点(U+0000 到 U+00FF)和常见的 ISO-8859-1(Latin 1) 编码完全一致,UTF-16 还是在常用的英文和西欧文本上浪费了大量的空间:每个 16 位的码点的高 8 位的值都会是 0。也许更重要的是,UTF-16 对一些老旧的代码造成了挑战,这些代码常常会假定文本是用 ASCII 编码的。Ken Thompson(他在 Unix 社区很有名) 和 Rob Pike 开发了 UTF-8 来弥补这些不足。

    UTF-8 使用一到四个字节来编码一个码点。从 0 到 127 的这些码点直接映射成 1 个字节(对于只包含这个范围字符的文本来说,这一点使得 UTF-8 和 ASCII 完全相同)。接下来的 1,920 个码点映射成 2 个字节,在 BMP 里所有剩下的码点需要 3 个字节。Unicode 的其他平面里的码点则需要 4 个字节。UTF-8 是基于 8 位的码元的,因此它并不需要关心字节顺序(不过仍有一些程序会在 UTF-8 文件里加上多余的 BOM)。

    有效率的空间使用(仅就西方语言来讲),以及不需要操心字节顺序问题使得 UTF-8 成为存储和交流 Unicode 文本方面的最佳编码。它也已经是文件格式、网络协议以及 Web API 领域里事实上的标准了。

    5. 如何计算真正的文本长度

    后面可以在https://xiaozhuanlan.com/topic/3175804962阅读

    相关文章

      网友评论

          本文标题:NSString 中 length 计算的是文本长度吗?

          本文链接:https://www.haomeiwen.com/subject/vspluhtx.html