Unicode in iOS

如何理解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ō)NSStringUnicode表示為16位,這完全蘋(píng)果的錯(cuò)誤,因?yàn)槲覀冎?code>Unicode是21位的,用16位怎么表示

  • 其實(shí)是NSString處理字符的任何方法都是以16位為基本處理單元

    • 那些少數(shù)的在Unicode中16位表示不過(guò)來(lái)的,在NSString中的length屬性就不是字符長(zhǎng)度了
  • 前文提到了組合字符序列,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
    

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容