1.各種編碼格式 原理以及發(fā)展歷史
1.1 ASCII碼
早起計(jì)算機(jī)發(fā)展初期,肯定是以字母為標(biāo)準(zhǔn)的,沒考慮世界上其他語言 如中文,日文韓文等,故此起初設(shè)計(jì)的字符集合也就比較簡(jiǎn)單,也就是如今的ASCII碼。
ASCII碼一共規(guī)定了128個(gè)字符的編碼,比如大寫的字母A是65(二進(jìn)制01000001)。這128個(gè)符號(hào)(包括32個(gè)不能打印出來的控制符號(hào)),只占用了一個(gè)字節(jié)的后面7位,最前面的1位統(tǒng)一規(guī)定為0。
1.2. 非ASCII碼
英語用128個(gè)符號(hào)編碼就夠了,但是用來表示其他語言,128個(gè)符號(hào)是不夠的。比如,在法語中,字母上方有注音符號(hào),它就無法用ASCII碼表示。需要擴(kuò)展編碼集合有些就使用ASCII碼保留碼有些,有些就使用兩個(gè)字節(jié)來表示如國標(biāo)GB2312就是兩個(gè)字節(jié)表示一個(gè)漢字。但是各個(gè)地區(qū)國家各自為政發(fā)展出來了很多不同的非ASCII碼體系,如臺(tái)灣與大陸編碼都不一樣,這導(dǎo)致溝通成本很大,故此迫切需要一個(gè)標(biāo)準(zhǔn)化的符號(hào)集合,Unicode孕育而生。
1.3.Unicode
Unicode只是一個(gè)符號(hào)集合,規(guī)定了二進(jìn)制代碼,但是并沒規(guī)定具體存儲(chǔ)方式。
對(duì)應(yīng)具體的漢字有可能其Unicode需要二個(gè)字節(jié)來保存其轉(zhuǎn)換出來的二進(jìn)制數(shù),也有可能需要3個(gè)字節(jié),4個(gè)字節(jié)才能夠保存,大部分漢字是需要4個(gè)字節(jié)來表示。
比如,漢字“嚴(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é),甚至更多。
這里就有兩個(gè)嚴(yán)重的問題,第一個(gè)問題是,如何才能區(qū)別unicode和ascii?計(jì)算機(jī)怎么知道三個(gè)字節(jié)表示一個(gè)符號(hào),而不是分別表示三個(gè)符號(hào)呢?第二個(gè)問題是,我們已經(jīng)知道,英文字母只用一個(gè)字節(jié)表示就夠了,如果unicode統(tǒng)一規(guī)定,每個(gè)符號(hào)用三個(gè)或四個(gè)字節(jié)表示,那么每個(gè)英文字母前都必然有二到三個(gè)字節(jié)是0,這對(duì)于存儲(chǔ)來說是極大的浪費(fèi),文本文件的大小會(huì)因此大出二三倍,這是無法接受的。
它們?cè)斐傻慕Y(jié)果是:1)出現(xiàn)了unicode的多種存儲(chǔ)方式,也就是說有許多種不同的二進(jìn)制格式,可以用來表示unicode。2)unicode在很長一段時(shí)間內(nèi)無法推廣,直到互聯(lián)網(wǎng)的出現(xiàn)。
1.4.UTF-8
UTF-8是Unicode的實(shí)現(xiàn)方式之一,其他實(shí)現(xiàn)方式還包括UTF-16和UTF-32,不過在互聯(lián)網(wǎng)上基本不用。
互聯(lián)網(wǎng)的普及,人們的快去要溝通越來越頻繁,強(qiáng)烈要求出現(xiàn)一種統(tǒng)一的編碼方式,UTF-8就此誕生。
UTF-8最大的一個(gè)特點(diǎn),就是它是一種變長的編碼方式。它可以使用1~4個(gè)字節(jié)表示一個(gè)符號(hào),根據(jù)不同的符號(hào)而變化字節(jié)長度。
UTF-8的編碼規(guī)則很簡(jiǎn)單,只有二條:
1)對(duì)于單字節(jié)的符號(hào),字節(jié)的第一位設(shè)為0,后面7位為這個(gè)符號(hào)的unicode碼。因此對(duì)于英語字母,UTF-8編碼和ASCII碼是相同的。
2)對(duì)于n字節(jié)的符號(hào)(n>1),第一個(gè)字節(jié)的前n位都設(shè)為1,第n+1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進(jìn)制位,全部為這個(gè)符號(hào)的unicode碼。
UTF-8 每字至少 1 byte,至多 4 bytes。1 byte 字符與 US-ASCII 相符。U+0800-07ff 是 2 bytes,0800-ffff 是 3 bytes,之后的是 4 bytes
下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。
Unicode符號(hào)范圍 | UTF-8編碼方式

下面,還是以漢字“嚴(yán)”為例,演示如何實(shí)現(xiàn)UTF-8編碼。
已知“嚴(yán)”的unicode是4E25(100111000100101),根據(jù)上表,可以發(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。
2.實(shí)際使用場(chǎng)景
2.1 需求:求中英文混合長度
從上可做字符串的長度取決于我們的字符編碼方式,如國標(biāo)GB2312那就一個(gè)漢字兩個(gè)字節(jié),字母一個(gè)字節(jié),如UTF-8,一個(gè)漢字是動(dòng)態(tài)的長度2個(gè)字節(jié)到4個(gè)字節(jié)之間,千萬別再說一個(gè)漢字在UTF-8這種unicode實(shí)現(xiàn)方式上是3個(gè)字節(jié)了,日韓字符集合是四個(gè)字節(jié),當(dāng)然常用的中文大部分落在3個(gè)字節(jié)范圍內(nèi)。所以說標(biāo)準(zhǔn)的約定很重要,客戶端內(nèi)部的約定,客戶端與服務(wù)器端的約定。只有形成了標(biāo)準(zhǔn)才能交流實(shí)現(xiàn)??蛻舳伺c服務(wù)器端標(biāo)準(zhǔn)不同話,各自算出的長度都不一樣,如某些場(chǎng)景需要截?cái)嘧址?,那么就容易出問題了,客戶端明明告訴服務(wù)器端截取多少個(gè),可因?yàn)闃?biāo)準(zhǔn)的不同多截了或者少截了,導(dǎo)致一些bug。
NSString * text = @"1234劉a";
NSInteger length = [text length]; //length值為6,length僅僅返回字符個(gè)數(shù)而不是存儲(chǔ)空間大小
NSInteger length_utf8 = [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; //length_utf8值為8
NSString * text1 = @"劉";
NSInteger length_utf8 = [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding];//length_utf8值為3
可見在NSUTF8StringEncoding存儲(chǔ)形式下的unicode,中文“劉”字,占存儲(chǔ)空間為3個(gè)字節(jié),字符與數(shù)字均為一個(gè)字節(jié)