寫在前面
在開發(fā)中我們經(jīng)常會(huì)遇到限制字符串長(zhǎng)度問題莱睁,如果用的是 NSString
很自然使用字符串屬性 length
來協(xié)助判斷掂铐,比如
@"我是字符串a(chǎn)bc".length < 10
但是如果字符串包含 emoji 表情的時(shí)候發(fā)現(xiàn)長(zhǎng)度會(huì)不一樣, 比如
var str1InOC: NSString = "??";
var str2InOC: NSString = "????";
var str3InOC: NSString = "??????";
var str4InOC: NSString = "????????";
print(
"str1InOC:", str1InOC.length,
"str2InOC:", str2InOC.length,
"str3InOC:", str3InOC.length,
"str4InOC:", str4InOC.length,
);
/*
輸出:
str1InOC: 2 str2InOC: 4 str3InOC: 7 str4InOC: 11
*/
在弄清原因前埋合,需要先來研究一下字符編碼問題备徐。
ASCII 字符集
我們知道,計(jì)算機(jī)內(nèi)部甚颂,所有信息最終都是一個(gè)二進(jìn)制值蜜猾。每一個(gè)二進(jìn)制位(bit)有0和1兩種狀態(tài),因此八個(gè)二進(jìn)制位就可以組合出256種狀態(tài)振诬,這被稱為一個(gè)字節(jié)(byte)蹭睡。也就是說,一個(gè)字節(jié)一共可以用來表示256種不同的狀態(tài)赶么,每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)符號(hào)肩豁,就是256個(gè)符號(hào),從00000000到11111111辫呻。
上個(gè)世紀(jì)60年代清钥,美國(guó)制定了一套字符編碼,對(duì)英語字符與二進(jìn)制位之間的關(guān)系放闺,做了統(tǒng)一規(guī)定祟昭。這被稱為 ASCII 碼,一直沿用至今怖侦。
ASCII 碼一共規(guī)定了128個(gè)字符的編碼篡悟,比如空格SPACE是32(二進(jìn)制00100000)谜叹,大寫的字母A是65(二進(jìn)制01000001)。這128個(gè)符號(hào)(包括32個(gè)不能打印出來的控制符號(hào))恰力,只占用了一個(gè)字節(jié)的后面7位叉谜,最前面的一位統(tǒng)一規(guī)定為0。
ASCII 字符集的局限性
英語用128個(gè)符號(hào)編碼就夠了踩萎,但是用來表示其他語言停局,128個(gè)符號(hào)是不夠的。比如香府,在法語中董栽,字母上方有注音符號(hào),它就無法用 ASCII 碼表示企孩。于是锭碳,一些歐洲國(guó)家就決定,利用字節(jié)中閑置的最高位編入新的符號(hào)勿璃。比如擒抛,法語中的é的編碼為130(二進(jìn)制10000010)。這樣一來补疑,這些歐洲國(guó)家使用的編碼體系歧沪,可以表示最多256個(gè)符號(hào)。
但是莲组,這里又出現(xiàn)了新的問題诊胞。不同的國(guó)家有不同的字母,因此锹杈,哪怕它們都使用256個(gè)符號(hào)的編碼方式撵孤,代表的字母卻不一樣。比如竭望,130在法語編碼中代表了é邪码,在希伯來語編碼中卻代表了字母Gimel (?),在俄語編碼中又會(huì)代表另一個(gè)符號(hào)咬清。但是不管怎樣霞扬,所有這些編碼方式中,0--127表示的符號(hào)是一樣的枫振,不一樣的只是128--255的這一段。
至于亞洲國(guó)家的文字萤彩,使用的符號(hào)就更多了粪滤,漢字就多達(dá)10萬左右。一個(gè)字節(jié)只能表示256種符號(hào)雀扶,肯定是不夠的杖小,就必須使用多個(gè)字節(jié)表達(dá)一個(gè)符號(hào)肆汹。比如,簡(jiǎn)體中文常見的編碼方式是 GBK予权,使用兩個(gè)字節(jié)表示一個(gè)漢字昂勉,后面會(huì)詳細(xì)講解。
其他語種也都有自己的字符集扫腺,這樣一來適配各種語種變得異常繁瑣岗照。
Unicode 字符集
Unicode,中文又稱萬國(guó)碼笆环、國(guó)際碼攒至、統(tǒng)一碼、單一碼躁劣,是計(jì)算機(jī)科學(xué)領(lǐng)域的業(yè)界標(biāo)準(zhǔn)迫吐。它整理、編碼了世界上大部分的文字系統(tǒng)账忘,使得電腦可以用更為簡(jiǎn)單的方式來呈現(xiàn)和處理文字志膀。
Unicode的編碼從U+0000到U+10FFFF,共有1,112,064個(gè)碼位(code point)可用來映射字符鳖擒。Unicode的編碼空間可以劃分為17個(gè)平面(plane)溉浙,每個(gè)平面包含2^16(65,536)個(gè)碼位。17個(gè)平面的碼位可表示為從U+xx0000到U+xxFFFF败去,其中xx表示十六進(jìn)制值從0x00到0x10放航,共計(jì)17個(gè)平面。第一個(gè)平面稱為基本多語言平面(Basic Multilingual Plane, BMP)圆裕,或稱第零平面(Plane 0)广鳍,其他平面稱為輔助平面(Supplementary Planes)。
一般Unicode的碼位表示成U+XXXXXX 的形式吓妆,X 代表一個(gè)十六制數(shù)字赊时,表示形式的范圍在 4-6 位之間,也就是U+0000 ~ U+10FFFF間行拢。當(dāng)碼位值不足 4 位時(shí)前面補(bǔ) 0 補(bǔ)足 4 位祖秒,超過則按是幾位就是幾位。
平面 | 始末字符值 | 中文名稱 | 英文名稱 |
---|---|---|---|
0號(hào)平面 | U+0000-U+FFFF | 基本多文種平面 | Basic Multilingual Plane舟奠,簡(jiǎn)稱BMP |
1號(hào)平面 | U+10000-U+1FFFF | 多文種補(bǔ)充平面 | Supplementary Multilingual Plane竭缝,簡(jiǎn)稱SMP |
2號(hào)平面 | U+20000-U+2FFFF | 表意文字補(bǔ)充平面 | Supplementary Ideographic Plane,簡(jiǎn)稱SIP |
3號(hào)平面 | U+30000-U+3FFFF | 表意文字第三平面 | Tertiary Ideographic Plane沼瘫,簡(jiǎn)稱TIP |
4號(hào)平面 至 13號(hào)平面 | U+40000-U+DFFFF | 尚未使用 | |
14號(hào)平面 | U+E0000-U+EFFFF | 特別用途補(bǔ)充平面 | Supplementary Special-purpose Plane抬纸,簡(jiǎn)稱SSP |
15號(hào)平面 | U+F0000-U+FFFFF | 保留作為私人使用區(qū)(A區(qū)) | Private Use Area-A,簡(jiǎn)稱PUA-A |
16號(hào)平面 | U+100000-U+10FFFF | 保留作為私人使用區(qū)(B區(qū)) | Private Use Area-B耿戚,簡(jiǎn)稱PUA-B |
其他更多平面映射詳情可以查看Unicode字符平面映射
Unicode 除了使用單個(gè)碼點(diǎn)表示 Emoji湿故,還允許多個(gè)碼點(diǎn)組合表示一個(gè) Emoji阿趁。其中的一種方式是"零寬度連接符"(ZERO WIDTH JOINER,縮寫 ZWJ)U+200D坛猪。舉例來說脖阵,下面是三個(gè) Emoji 的碼點(diǎn)。
U+1F468:男人
U+1F469:女人
U+1F467:女孩
上面三個(gè)碼點(diǎn)使用U+200D連接起來墅茉,U+1F468 U+200D U+1F469 U+200D U+1F467命黔,就會(huì)顯示為一個(gè) Emoji ??????,表示他們組成的家庭躁锁。如果用戶的系統(tǒng)不支持這種方法纷铣,就還是顯示為三個(gè)獨(dú)立的 Emoji ??????
UTF-32、UTF-16战转、UTF-8 編碼
需要注意的是搜立,Unicode 只是一個(gè)符號(hào)集,它只規(guī)定了符號(hào)的二進(jìn)制代碼槐秧,卻沒有規(guī)定這個(gè)二進(jìn)制代碼應(yīng)該如何存儲(chǔ)啄踊。
比如,漢字嚴(yán)的 Unicode 是十六進(jìn)制數(shù)4E25刁标,轉(zhuǎn)換成二進(jìn)制數(shù)足足有15位(100111000100101)颠通,也就是說,這個(gè)符號(hào)的表示至少需要2個(gè)字節(jié)膀懈。表示其他更大的符號(hào)顿锰,可能需要3個(gè)字節(jié)或者4個(gè)字節(jié),甚至更多启搂。
于是有了Unicode轉(zhuǎn)換格式硼控,縮寫為UTF(Unicode Transformation Formats),用來處理編碼問題胳赌。
UTF-32編碼
UTF-32是一種用于編碼Unicode的協(xié)定牢撼,該協(xié)定使用32位比特對(duì)每個(gè)Unicode碼位進(jìn)行編碼(但前導(dǎo)比特?cái)?shù)必須為零,故僅能表示2^21個(gè)Unicode碼位)疑苫。與其他可變長(zhǎng)度的Unicode轉(zhuǎn)換格式(UTF)相比熏版,UTF-32編碼長(zhǎng)度是固定的,UTF-32中的每個(gè)32位值代表一個(gè)Unicode碼位捍掺,并且與該碼位的數(shù)值完全一致撼短。
優(yōu)點(diǎn):可以直接由Unicode碼位來索引。在編碼序列中查找第N個(gè)編碼是一個(gè)常數(shù)時(shí)間操作挺勿。相比之下阔加,其他可變長(zhǎng)度編碼需要進(jìn)行循序訪問操作才能在編碼序列中找到第N個(gè)編碼。這使得在計(jì)算機(jī)程序設(shè)計(jì)中满钟,編碼序列中的字符位置可以用一個(gè)整數(shù)來表示胜榔,整數(shù)加一即可得到下一個(gè)字符的位置,就和ASCII字符串一樣簡(jiǎn)單湃番。
缺點(diǎn):每個(gè)碼位使用四個(gè)字節(jié)夭织,空間浪費(fèi)較多。在大多數(shù)文本中吠撮,非基本多文種平面的字符非常罕見尊惰,這使得UTF-32所需空間接近UTF-16的兩倍和UTF-8的四倍(具體取決于文本中ASCII字符的比例)。
盡管每一個(gè)碼位使用固定長(zhǎng)度的字節(jié)看似方便泥兰,但UTF-32并不如其它Unicode編碼使用廣泛弄屡。與UTF-8及UTF-16相比,UTF-32更容易遭到截?cái)嘈<词故褂昧?定寬"字體膀捷,在大多數(shù)情況下用UTF-32計(jì)算顯示字符串的寬度也并不比其他編碼更加容易。主要原因是削彬,存在著一個(gè)字符位置會(huì)有多于一種可能的碼點(diǎn)(結(jié)合字符)或一個(gè)碼點(diǎn)用多于一個(gè)字符位置(如CJK表意字符)全庸。結(jié)合符號(hào)也意味著,文書編輯者不能將一個(gè)碼位視同一個(gè)編輯上的單位融痛。
UTF-16編碼
UTF-16使用16位比特對(duì)每個(gè)Unicode碼位進(jìn)行編碼映射壶笼,0號(hào)平面內(nèi)的碼位可以直接映射;其他平面的字符代理至0號(hào)平面的U+D800到U+DFFF(從U+D800到U+DFFF之間的碼位區(qū)段是永久保留不映射到Unicode字符)雁刷。
接下來分析其他平面從U+10000到U+10FFFF的碼位如何映射至0號(hào)平面的U+D800到U+DFFF覆劈。
輔助平面(Supplementary Planes)中的碼位,在UTF-16中被編碼為一對(duì)16比特長(zhǎng)的碼元(即32位沛励,4字節(jié))责语,稱作代理對(duì)(Surrogate Pair),具體方法是:
- 碼位減去 0x10000侯勉,得到的值的范圍為20比特長(zhǎng)的 0...0xFFFFF鹦筹。
- 高位的10比特的值(值的范圍為 0x000...0x3FF)被加上 0xD800 得到第一個(gè)碼元或稱作高位代理(high surrogate),值的范圍是 0xD800...0xDBFF址貌。由于高位代理比低位代理的值要小铐拐,所以為了避免混淆使用,Unicode標(biāo)準(zhǔn)現(xiàn)在稱高位代理為前導(dǎo)代理(lead surrogates)练对。
- 低位的10比特的值(值的范圍也是 0x000...0x3FF)被加上 0xDC00 得到第二個(gè)碼元或稱作低位代理(low surrogate)遍蟋,現(xiàn)在值的范圍是0xDC00...0xDFFF。由于低位代理比高位代理的值要大螟凭,所以為了避免混淆使用虚青,Unicode標(biāo)準(zhǔn)現(xiàn)在稱低位代理為后尾代理(trail surrogates)。
上述算法可理解為:輔助平面中的碼位從U+10000到U+10FFFF螺男,共計(jì)FFFFF個(gè)棒厘,即2^20 = 1,048,576個(gè)纵穿,需要20位來表示。如果用兩個(gè)16位長(zhǎng)的整數(shù)組成的序列來表示奢人,第一個(gè)整數(shù)(稱為前導(dǎo)代理)要容納上述20位的前10位谓媒,第二個(gè)整數(shù)(稱為后尾代理)容納上述20位的后10位。還要能根據(jù)16位整數(shù)的值直接判明屬于前導(dǎo)整數(shù)代理的值的范圍(2^10 = 1024)何乎,還是后尾整數(shù)代理的值的范圍(也是2^10 = 1024)句惯。因此,需要在基本多語言平面中保留不對(duì)應(yīng)于Unicode字符的2048個(gè)碼位支救,就足以容納前導(dǎo)代理與后尾代理所需要的編碼空間抢野。這對(duì)于基本多語言平面總計(jì)65536個(gè)碼位來說,僅占3.125%各墨。
由于前導(dǎo)代理指孤、后尾代理、BMP中的有效字符的碼位欲主,三者互不重疊邓厕,搜索是簡(jiǎn)單的:一個(gè)字符編碼的一部分不可能與另一個(gè)字符編碼的不同部分相重疊。這意味著UTF-16是自同步(self-synchronizing)的:可以通過僅檢查一個(gè)碼元來判定給定字符的下一個(gè)字符的起始碼元扁瓢。UTF-8也有類似優(yōu)點(diǎn)详恼,但許多早期的編碼模式就不是這樣,必須從頭開始分析文本才能確定不同字符的碼元的邊界引几。
編碼例子:以U+10437編碼(??)為例:
- 0x10437 減去 0x10000昧互,結(jié)果為0x00437,二進(jìn)制為 0000 0000 0100 0011 0111
- 分割它的上10位值和下10位值(使用二進(jìn)制):0000 0000 01 和 00 0011 0111
- 添加 0xD800 到上值伟桅,以形成高位:0xD800 + 0x0001 = 0xD801
- 添加 0xDC00 到下值敞掘,以形成低位:0xDC00 + 0x0037 = 0xDC37
最終:U+10437 通過 UTF-16 大尾端為 D801DC37
相比 UTF-32,UTF-16 節(jié)省了一半的空間楣铁,但是不兼容 ASCII 碼玖雁。而已還需要處理小尾端(Little endian) 和 大尾端(Big endian),后面會(huì)單獨(dú)解釋盖腕。
UTF-8編碼
UTF-8(8-bit Unicode Transformation Format)是一種針對(duì)Unicode的可變長(zhǎng)度字符編碼赫冬,也是一種前綴碼。它可以用一至四個(gè)字節(jié)對(duì)Unicode字符集中的所有有效編碼點(diǎn)進(jìn)行編碼溃列,屬于Unicode標(biāo)準(zhǔn)的一部分劲厌。由于較小值的編碼點(diǎn)一般使用頻率較高,直接使用Unicode編碼效率低下听隐,大量浪費(fèi)內(nèi)存空間补鼻。UTF-8就是為了解決向后兼容ASCII碼而設(shè)計(jì),Unicode中前128個(gè)字符,使用與ASCII碼相同的二進(jìn)制值的單個(gè)字節(jié)進(jìn)行編碼风范,而且字面與ASCII碼的字面一一對(duì)應(yīng)咨跌,這使得原來處理ASCII字符的軟件無須或只須做少部分修改,即可繼續(xù)使用硼婿。因此虑润,它逐漸成為電子郵件、網(wǎng)頁(yè)及其他存儲(chǔ)或發(fā)送文字優(yōu)先采用的編碼方式加酵。
UTF-8使用一至六個(gè)字節(jié)為每個(gè)字符編碼(盡管如此,2003年11月UTF-8被RFC 3629重新規(guī)范哭当,只能使用原來Unicode定義的區(qū)域猪腕,U+0000到U+10FFFF,也就是說最多四個(gè)字節(jié)):
- 128個(gè)US-ASCII字符只需一個(gè)字節(jié)編碼(Unicode范圍由U+0000至U+007F)钦勘。
- 帶有附加符號(hào)的拉丁文陋葡、希臘文、西里爾字母彻采、亞美尼亞語腐缤、希伯來文、阿拉伯文肛响、敘利亞文及它拿字母則需要兩個(gè)字節(jié)編碼(Unicode范圍由U+0080至U+07FF)岭粤。
- 其他基本多文種平面(BMP)中的字符(這包含了大部分常用字,如大部分的漢字)使用三個(gè)字節(jié)編碼(Unicode范圍由U+0800至U+FFFF)特笋。
- 其他極少使用的Unicode 輔助平面的字符使用四至六字節(jié)編碼(Unicode范圍由U+10000至U+1FFFFF使用四字節(jié)剃浇,Unicode范圍由U+200000至U+3FFFFFF使用五字節(jié),Unicode范圍由U+4000000至U+7FFFFFFF使用六字節(jié))猎物。
對(duì)上述提及的第四種字符而言虎囚,UTF-8使用四至六個(gè)字節(jié)來編碼似乎太耗費(fèi)資源了。但UTF-8對(duì)所有常用的字符都可以用三個(gè)字節(jié)表示蔫磨,而且它的另一種選擇淘讥,UTF-16編碼,對(duì)前述的第四種字符同樣需要四個(gè)字節(jié)來編碼堤如,所以要決定UTF-8或UTF-16哪種編碼比較有效率蒲列,還要視所使用的字符的分布范圍而定。不過煤惩,如果使用一些傳統(tǒng)的壓縮系統(tǒng)嫉嘀,比如DEFLATE,則這些不同編碼系統(tǒng)間的的差異就變得微不足道了魄揉。
UTF-8編碼規(guī)則:
- 對(duì)于UTF-8編碼中的任意字節(jié)B剪侮,如果B的第一位為0,則B獨(dú)立的表示一個(gè)字符(ASCII碼);
- 如果B的第一位為1瓣俯,第二位為0杰标,則B為一個(gè)多字節(jié)字符中的一個(gè)字節(jié)(非ASCII字符);
- 如果B的前兩位為1彩匕,第三位為0腔剂,則B為兩個(gè)字節(jié)表示的字符中的第一個(gè)字節(jié);
- 如果B的前三位為1驼仪,第四位為0掸犬,則B為三個(gè)字節(jié)表示的字符中的第一個(gè)字節(jié);
- 如果B的前四位為1绪爸,第五位為0湾碎,則B為四個(gè)字節(jié)表示的字符中的第一個(gè)字節(jié);
因此奠货,對(duì)UTF-8編碼中的任意字節(jié)介褥,根據(jù)第一位,可判斷是否為ASCII字符递惋;根據(jù)前二位柔滔,可判斷該字節(jié)是否為一個(gè)字符編碼的第一個(gè)字節(jié);根據(jù)前四位(如果前兩位均為1)萍虽,可確定該字節(jié)為字符編碼的第一個(gè)字節(jié)睛廊,并且可判斷對(duì)應(yīng)的字符由幾個(gè)字節(jié)表示;根據(jù)前五位(如果前四位為1)贩挣,可判斷編碼是否有錯(cuò)誤或數(shù)據(jù)傳輸過程中是否有錯(cuò)誤喉前。
小結(jié):
Unicode符號(hào)范圍(十六進(jìn)制) | UTF-8編碼方式(二進(jìn)制) |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
比如:嚴(yán)的 Unicode 是4E25(100111000100101)編碼
可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800 - 0000 FFFF),因此嚴(yán)的 UTF-8 編碼需要三個(gè)字節(jié)王财,即格式是1110xxxx 10xxxxxx 10xxxxxx卵迂。然后,從嚴(yán)的最后一個(gè)二進(jìn)制位開始绒净,依次從后向前填入格式中的x见咒,多出的位補(bǔ)0。這樣就得到了挂疆,嚴(yán)的 UTF-8 編碼是11100100 10111000 10100101改览,轉(zhuǎn)換成十六進(jìn)制就是E4B8A5。
關(guān)于小尾端(Little endian) 和 大尾端(Big endian)
UTF-32和UTF-16在不同的計(jì)算機(jī)系統(tǒng)會(huì)以不同的順序保存字節(jié)缤言。這意味著字符U+4E2D在UTF-16編碼方式下可能被保存為4E 2D或者2D 4E宝当,這取決于該系統(tǒng)使用的是大尾端(big-endian)還是小尾端(little-endian)。只要文檔沒有離開你的計(jì)算機(jī)胆萧,它還是安全的——同一臺(tái)電腦上的不同程序使用相同的字節(jié)順序(byte order)庆揩。但是當(dāng)我們需要在系統(tǒng)之間傳輸這個(gè)文檔的時(shí)候俐东,也許在萬維網(wǎng)中,我們就需要一種方法來指示當(dāng)前我們的字節(jié)是怎樣存儲(chǔ)的订晌。不然的話虏辫,接收文檔的計(jì)算機(jī)就無法知道這兩個(gè)字節(jié)4E 2D表達(dá)的到底是U+4E2D還是U+2D4E。
Unicode 規(guī)范定義锈拨,每一個(gè)文件的最前面分別加入一個(gè)表示編碼順序的字符砌庄,這個(gè)字符的名字叫做"零寬度非換行空格"(zero width no-break space),用FEFF表示奕枢。
在UTF-16編碼中娄昆,如果文本文件的頭兩個(gè)字節(jié)是FE FF,就表示該文件采用大尾端缝彬;如果頭兩個(gè)字節(jié)是FF FE稿黄,就表示該文件采用小尾端。
編碼 | 小尾端(Little endian) | 大尾端(Big endian) |
---|---|---|
UTF-16 | FFFE | FEFF |
UTF-32 | FFFE0000 | 0000FEFF |
GB2312跌造、Big5、GBK族购、GB18030 字符集
GB2312字符集
GB2312 最早一版的中文編碼壳贪,每個(gè)字占據(jù)兩個(gè)字節(jié)。由于要和ASCII兼容寝杖,那這兩個(gè)字節(jié)最高位不可以為0了(否則和ASCII會(huì)有沖突)违施。在GB2312中收錄了6763個(gè)漢字以及682個(gè)特殊符號(hào),已經(jīng)囊括了生活中最常用的所有漢字瑟幕。
GB2312中也收錄了英文字母和數(shù)字等符號(hào)(ASCII碼中也有這些符號(hào))磕蒲,并且仍然是以倆字節(jié)編碼,于是GB2312中的英文字母和數(shù)字等就成了我們平常所說的全角符號(hào)只盹,而ASCII碼的符號(hào)就叫做半角符號(hào)辣往。
Big5字符集
Big5是由臺(tái)灣財(cái)團(tuán)法人信息產(chǎn)業(yè)策進(jìn)會(huì)為五大中文套裝軟件(并因此得名Big-5)所設(shè)計(jì)的中文共通內(nèi)碼,在1983年12月完成公告殖卑。那個(gè)之前還沒有繁體字編碼站削,GB2312又不含繁體字,因此才有了Big-5
GBK字符集
GBK 即漢字內(nèi)碼擴(kuò)展規(guī)范孵稽,K為漢語拼音 Kuo Zhan(擴(kuò)展)中“擴(kuò)”字的聲母许起。英文全稱 Chinese Internal Code Specification。
微軟利用了GB2312中未使用的編碼空間菩鲜,并且收錄了GB13000中的全部字符园细,從而定制了GBK編碼(雖然收錄了GB13000的全部字符,但是編碼方式并不相同)接校,并且實(shí)現(xiàn)于Windows95中文版中猛频。GBK自身并非國(guó)家標(biāo)準(zhǔn),不過1995年由國(guó)標(biāo)局等機(jī)構(gòu)確定為“技術(shù)規(guī)范指導(dǎo)性文件”。
GBK 共收入 21886 個(gè)漢字和圖形符號(hào)伦乔,包括:
- GB2312 中的全部漢字厉亏、非漢字符號(hào)。
- BIG5 中的全部漢字烈和。
- 與 ISO 10646 相應(yīng)的國(guó)家標(biāo)準(zhǔn) GB 13000 中的其它 CJK 漢字爱只,以上合計(jì) 20902 個(gè)漢字。
- 其它漢字招刹、部首恬试、符號(hào),共計(jì) 984 個(gè)疯暑。
簡(jiǎn)單地說:GBK是從GB2312擴(kuò)展而來的训柴,支持繁體,并且兼容GB2312妇拯。
GB18030字符集
全稱:國(guó)家標(biāo)準(zhǔn) GB18030-2005《信息技術(shù)中文編碼字符集》
GB2312和GBK都是用兩個(gè)字節(jié)來編碼的幻馁,就算用完所有的位(256*256=65536)也不夠?yàn)樗械臐h字編碼。于是就有了目前最新的GB18030越锈,它采用類似UTF-8的編碼方式進(jìn)行編碼(每個(gè)字符的編碼可以是1仗嗦、2或4個(gè)字節(jié)),擁有上百萬個(gè)編碼空間甘凭,足以支持中日韓三國(guó)所有漢字稀拐,并且還可以支持國(guó)內(nèi)少數(shù)民族的文字。
GB18030 與 GB2312-1980 和 GBK 兼容丹弱,共收錄漢字70244個(gè)德撬。
- 與 UTF-8 相同,采用多字節(jié)編碼躲胳,每個(gè)字可以由 1 個(gè)蜓洪、2 個(gè)或 4 個(gè)字節(jié)組成。
- 編碼空間龐大坯苹,最多可定義 161 萬個(gè)字符蝠咆。
- 支持中國(guó)國(guó)內(nèi)少數(shù)民族的文字,不需要?jiǎng)佑迷熳謪^(qū)北滥。
- 漢字收錄范圍包含繁體漢字以及日韓漢字刚操。
GB18030 編碼是一二四字節(jié)變長(zhǎng)編碼。
- 單字節(jié)再芋,其值從 0 到 0x7F菊霜,與 ASCII 編碼兼容。
- 雙字節(jié)济赎,第一個(gè)字節(jié)的值從 0x81 到 0xFE鉴逞,第二個(gè)字節(jié)的值從 0x40 到 0xFE(不包括0x7F)记某,與 GBK 標(biāo)準(zhǔn)兼容。
- 四字節(jié)构捡,第一個(gè)字節(jié)的值從 0x81 到 0xFE液南,第二個(gè)字節(jié)的值從 0x30 到 0x39,第三個(gè)字節(jié)從0x81 到 0xFE勾徽,第四個(gè)字節(jié)從 0x30 到 0x39滑凉。
iOS 字符長(zhǎng)度問題
回到最開始的問題上,NSString 是采用 UTF-16 進(jìn)行編碼的喘帚,length 方法的返回值也是字符串包含的碼元個(gè)數(shù)(而不是字符個(gè)數(shù))畅姊,所以這就可以解釋為什么emoji表情的長(zhǎng)度會(huì)有問題。
enumerateSubstringsInRange:options:usingBlock:
方法吹由。這個(gè)方法把 Unicode 抽象的地方隱藏了若未,能讓你輕松地循環(huán)字符串里的組合字符串、單詞倾鲫、行粗合、句子或段落。你甚至可以加上 NSStringEnumerationLocalized
這個(gè)選項(xiàng)乌昔,這樣可以在確定詞語間和句子間的邊界時(shí)把用戶所在的區(qū)域考慮進(jìn)去舌劳。要遍歷單個(gè)字符,把參數(shù)指定為 NSStringEnumerationByComposedCharacterSequences
NSString *s = @"123??????";
NSRange fullRange = NSMakeRange(0, [s length]);
__block NSInteger count = 0;
[s enumerateSubstringsInRange:fullRange
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop)
{
count++;
}];
NSLog(@"%ld", count);
/*
輸出:
5
*/