如何理解Unicode
- Unicode是個(gè)通用字符集,涵蓋了全世界所有字符
- 總范圍從
U+0000~U+10FFFF,十六進(jìn)制表示,共1114112個(gè)字符,占用21位 - 通過(guò)每個(gè)字符,在Unicode字符集中有個(gè)一個(gè)碼點(diǎn)(code points)對(duì)應(yīng),如
U+F8FF表示蘋(píng)果的icon - Unicode之規(guī)定了字符到碼點(diǎn)的對(duì)應(yīng),但并沒(méi)有規(guī)定在機(jī)器中如何存儲(chǔ),即1個(gè)字節(jié)肯定無(wú)法存儲(chǔ)所有字符,如果有多個(gè)字節(jié),程序去讀這些字節(jié)時(shí),遇到多個(gè)字節(jié)表示一個(gè)字符的情況,程序怎么知道要一次多去多個(gè)字節(jié)
- Unicode所有的字符被抽象成17個(gè)平面,每個(gè)平面包含了不同的字符Unicode表達(dá)
- 0號(hào)平面叫做BMP(Basic Multilingual Plane),基本多文種平面,包括了常用的字符
- 為了兼容一些老的編碼系統(tǒng),有些字符看上去是一個(gè),其實(shí)在Unicode中有多種表示形式
- 如
é,既可以用U+00E9表示,也可以用U+0065(小寫(xiě)字母e)加上U+0301(尖括號(hào))合成表示,這種叫做組合字符序列字符序列(ComposedString) - 上面
é的例子,外觀一樣、含義也一樣的情況下,這兩個(gè)叫做標(biāo)準(zhǔn)等價(jià)(canonically equivalent) - 有的情況,外觀一樣,但意義卻不同,比如小寫(xiě)的拉丁連字符(
U+FB00)兩個(gè)小寫(xiě)拉丁字母ff(U+0066 U+0066),外觀相同,但卻含義不同。這種只能叫做相容等價(jià)(compatibility equivalence)
- 如
UTF(Unicode Transformation Formats)
- Unicode轉(zhuǎn)換格式,這是將Unicode真正用到程序中的一步,即規(guī)定了在內(nèi)存、磁盤(pán)中如何存儲(chǔ)Unicode碼
- UTF-32,每個(gè)字符的Unicode碼點(diǎn),都用32位來(lái)表示,由于32位>21位,所以程序自然知道如何讀取字符,但浪費(fèi)空間
- 這里的32位,或者后面的UTF-16的16位,稱(chēng)作碼元(code unit)
- UTF-16,每個(gè)Unicode碼點(diǎn)用1-2個(gè)16位來(lái)表示,這時(shí)候就得規(guī)定讀取順序了,即字節(jié)順序標(biāo)記(BOM-Byte Order Mask)
- 所以,當(dāng)使用UTF-16編碼時(shí),一定要標(biāo)示字節(jié)讀取順序,一般寫(xiě)到文本開(kāi)頭位置的兩個(gè)字節(jié)。默認(rèn)不寫(xiě)的話(huà)是使用
高字節(jié)順序 - UTF-8
- 用1-4個(gè)字節(jié)標(biāo)示Unicode
- UTF-8和ASCII的所有碼點(diǎn)完全重合
- 不需要考慮字節(jié)讀取順序?UTF-8規(guī)定了字節(jié)讀取順序
NSString
-
iOS中表示Unicode中BMP的字符用
\uxxxx,非BMP的要用\Uxxxxxxxx表示NSString *s = @"\U0001F30D";//?? ``` C99不允許標(biāo)準(zhǔn)C字符集中的字符用
\uxxxx這種形式表示,所以在iOS中寫(xiě)\u0041(Unicode表示大寫(xiě)字母A)是不允許蘋(píng)果文檔中說(shuō)
NSString將Unicode表示為16位,這完全蘋(píng)果的錯(cuò)誤,因?yàn)槲覀冎?code>Unicode是21位的,用16位怎么表示-
其實(shí)是
NSString處理字符的任何方法都是以16位為基本處理單元- 那些少數(shù)的在Unicode中16位表示不過(guò)來(lái)的,在
NSString中的length屬性就不是字符長(zhǎng)度了
- 那些少數(shù)的在Unicode中16位表示不過(guò)來(lái)的,在
-
前文提到了組合字符序列,
NSString也提供了組合與非組合之間的轉(zhuǎn)換方法,用于在標(biāo)準(zhǔn)等價(jià)、相容等價(jià)幾種情況下進(jìn)行切換[test precomposedStringWithCanonicalMapping]; [test decomposedStringWithCanonicalMapping]; [test precomposedStringWithCompatibilityMapping]; [test decomposedStringWithCompatibilityMapping]; 官方建議使用
NSString時(shí),將字符串看做子字符串的序列,而不是字符的序列,因?yàn)樗淖址腿藗兛吹降恼J(rèn)為的字符不是一回事。但如果看做子字符串來(lái)處理,則其內(nèi)部的方法會(huì)很好的兼容各種問(wèn)題NSString中能夠保證返回正確字符數(shù)的方法是
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對(duì)Unicode的支持,不像OC中的NString。String的
count能夠很好的計(jì)算字符個(gè)數(shù),因?yàn)闀?huì)考慮到unicdoe各種組合的情況- 當(dāng)然也就可以把字符串當(dāng)做字符的序列了,單個(gè)字符也可以正確地進(jìn)行處理了
- 正因?yàn)閺?fù)雜的Unicode各種情況,Swift中獲取子串或索引時(shí),就不能通過(guò)整型值來(lái)獲取了。而是通過(guò)
String.Index類(lèi)型
- Swift中進(jìn)行字符或字符串等價(jià)判斷時(shí),Swift認(rèn)為
標(biāo)準(zhǔn)等價(jià)才是相等,相容等價(jià)并不相等let latinCapitalLetterA: Character = "\u{41}"http://大寫(xiě)拉丁字母A let cyrillicCapitalLetterA: Character = "\u{0410}"http://西里爾字母A print(latinCapitalLetterA + "-" + cyrillicCapitalLetterA) print(latinCapitalLetterA == cyrillicCapitalLetterA) //結(jié)果 A-A false