如何理解Unicode
- Unicode是个通用字符集,涵盖了全世界所有字符
- 总范围从
U+0000
~U+10FFFF
,十六进制表示,共1114112
个字符,占用21位 - 通过每个字符,在Unicode字符集中有个一个码点(code points)对应,如
U+F8FF
表示苹果的icon - Unicode之规定了字符到码点的对应,但并没有规定在机器中如何存储,即1个字节肯定无法存储所有字符,如果有多个字节,程序去读这些字节时,遇到多个字节表示一个字符的情况,程序怎么知道要一次多去多个字节
- Unicode所有的字符被抽象成17个平面,每个平面包含了不同的字符Unicode表达
- 0号平面叫做BMP(Basic Multilingual Plane),基本多文种平面,包括了常用的字符
- 为了兼容一些老的编码系统,有些字符看上去是一个,其实在Unicode中有多种表示形式
- 如
é
,既可以用U+00E9
表示,也可以用U+0065
(小写字母e)加上U+0301
(尖括号)合成表示,这种叫做组合字符序列字符序列(ComposedString) - 上面
é
的例子,外观一样、含义也一样的情况下,这两个叫做标准等价
(canonically equivalent) - 有的情况,外观一样,但意义却不同,比如小写的拉丁连字符(
U+FB00
)两个小写拉丁字母ff
(U+0066 U+0066
),外观相同,但却含义不同。这种只能叫做相容等价
(compatibility equivalence)
- 如
UTF(Unicode Transformation Formats)
- Unicode转换格式,这是将Unicode真正用到程序中的一步,即规定了在内存、磁盘中如何存储Unicode码
- UTF-32,每个字符的Unicode码点,都用32位来表示,由于32位>21位,所以程序自然知道如何读取字符,但浪费空间
- 这里的32位,或者后面的UTF-16的16位,称作码元(code unit)
- UTF-16,每个Unicode码点用1-2个16位来表示,这时候就得规定读取顺序了,即字节顺序标记(BOM-Byte Order Mask)
- 所以,当使用UTF-16编码时,一定要标示字节读取顺序,一般写到文本开头位置的两个字节。默认不写的话是使用
高字节顺序
- UTF-8
- 用1-4个字节标示Unicode
- UTF-8和ASCII的所有码点完全重合
- 不需要考虑字节读取顺序?UTF-8规定了字节读取顺序
NSString
-
iOS中表示Unicode中BMP的字符用
\uxxxx
,非BMP的要用\Uxxxxxxxx
表示NSString *s = @"\U0001F30D";// ```
C99不允许标准C字符集中的字符用
\uxxxx
这种形式表示,所以在iOS中写\u0041
(Unicode表示大写字母A)是不允许苹果文档中说
NSString
将Unicode
表示为16位,这完全苹果的错误,因为我们知道Unicode
是21位的,用16位怎么表示-
其实是
NSString
处理字符的任何方法都是以16位为基本处理单元- 那些少数的在Unicode中16位表示不过来的,在
NSString
中的length属性就不是字符长度了
- 那些少数的在Unicode中16位表示不过来的,在
-
前文提到了组合字符序列,
NSString
也提供了组合与非组合之间的转换方法,用于在标准等价
、相容等价
几种情况下进行切换[test precomposedStringWithCanonicalMapping]; [test decomposedStringWithCanonicalMapping]; [test precomposedStringWithCompatibilityMapping]; [test decomposedStringWithCompatibilityMapping];
官方建议使用
NSString
时,将字符串看做子字符串的序列
,而不是字符的序列
,因为它的字符和人们看到的认为的字符不是一回事。但如果看做子字符串来处理,则其内部的方法会很好的兼容各种问题NSString
中能够保证返回正确字符数的方法是
NSString *s = @"The weather on \U0001F30D is \U0001F31E today.";
// The weather on is today.
NSRange fullRange = NSMakeRange(0, [s length]);
[s enumerateSubstringsInRange:fullRange
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop)
{
NSLog(@"%@ %@", substring, NSStringFromRange(substringRange));
}];
Swift中的Unicode
- Swift中String对Unicode的支持,不像OC中的NString。String的
count
能够很好的计算字符个数,因为会考虑到unicdoe各种组合的情况- 当然也就可以把字符串当做字符的序列了,单个字符也可以正确地进行处理了
- 正因为复杂的Unicode各种情况,Swift中获取子串或索引时,就不能通过整型值来获取了。而是通过
String.Index
类型
- Swift中进行字符或字符串等价判断时,Swift认为
标准等价
才是相等,相容等价
并不相等let latinCapitalLetterA: Character = "\u{41}"//大写拉丁字母A let cyrillicCapitalLetterA: Character = "\u{0410}"//西里尔字母A print(latinCapitalLetterA + "-" + cyrillicCapitalLetterA) print(latinCapitalLetterA == cyrillicCapitalLetterA) //结果 A-A false
参考
- NSString 与 Unicode
- Characters and Grapheme Clusters
- String Section(Swift Language Guide)