淺談 UTF-8 編碼

ASCII、GBK展运、Unicode 與 UTF-8

在計(jì)算機(jī)內(nèi)部活逆,所有信息最終都是一個(gè)二進(jìn)制值。每一個(gè)二進(jìn)制位(bit)有 01 兩種狀態(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),從 0000000011111111所灸。

上個(gè)世紀(jì) 60 年代丽惶,美國(guó)制定了一套基于拉丁字母的計(jì)算機(jī)編碼系統(tǒng),用于顯示現(xiàn)代英文爬立。稱為 ASCIIAmerican Standard Code for Information Interchange钾唬,美國(guó)信息交換標(biāo)準(zhǔn)代碼),一直沿用至今侠驯。

ASCII 一共規(guī)定了 128 個(gè)字符的編碼抡秆,在計(jì)算機(jī)中常用一個(gè)字節(jié)表示,字節(jié)最前面的一位統(tǒng)一規(guī)定為0吟策,后面 7 位來表示具體的碼點(diǎn)(code point)儒士。值得一提的是在 ASCII 中碼點(diǎn)就是在 ASCII 字符集中的序號(hào),例如大寫的字母A在 ASCII 字符集中對(duì)應(yīng)的二進(jìn)制是01000001踊挠,而它的 ASCII 碼點(diǎn)為 65乍桂,剛好一一對(duì)應(yīng)。

雖然英語用 ASCII 編碼就夠了效床,但是對(duì)于其他語言睹酌,ASCII 是不夠的。例如漢字就多達(dá) 10 萬左右剩檀,因此中國(guó)政府就推出了 GB 2312信息交換用漢字編碼字符集·基本集憋沿,國(guó)標(biāo)),其中主要包含了兩部分沪猴,即編碼字符集和編碼方式辐啄。具體細(xì)節(jié)這里就不贅述,但是簡(jiǎn)單來說只有 UTF-16 這種編碼方式的 Unicode运嗜。值得一提的是 GB 2312 本身的字符集標(biāo)準(zhǔn)理論上最多可以表示 256 x 256 = 65536 個(gè)字符壶辜,所以實(shí)際上目前我們常用的是GBK《漢字內(nèi)碼擴(kuò)展規(guī)范(GBK)》1.0 版)這個(gè)字符集,不過 GBK 本身不是一個(gè)國(guó)標(biāo)担租,是微軟推出的一個(gè)擴(kuò)展(操作系統(tǒng)的發(fā)展遠(yuǎn)遠(yuǎn)超過國(guó)家制定標(biāo)準(zhǔn)的發(fā)展砸民,操作系統(tǒng)廠商不得不先解決人民的一個(gè)痛點(diǎn)),所以它并沒有后面的那個(gè)號(hào)奋救。

那么什么是剛才提到的 Unicode岭参?正如前面所說的中國(guó)政府推出了 GB 2312 字符集,那么其他國(guó)家尝艘、跨國(guó)公司自然也會(huì)推出自己的字符集演侯。如果我們把字符集想象成一個(gè)教室,每個(gè)課桌上坐的學(xué)生就是字符背亥,而每個(gè)學(xué)生的學(xué)號(hào)為碼點(diǎn)秒际,不難想象不同的教室會(huì)有各自給學(xué)生編學(xué)號(hào)的規(guī)則悬赏,同一個(gè)學(xué)生在不同的教室可能坐在不同的位置上,自然同一個(gè)學(xué)號(hào)在不同的教室找到的很有可能是不同的學(xué)生程癌。所以人們迫切需要一種規(guī)則舷嗡,可以把世界上所有的學(xué)生都放進(jìn)同一個(gè)教室,每個(gè)學(xué)生都有一個(gè)獨(dú)一無二的學(xué)號(hào)嵌莉,這樣就能方便的找到對(duì)應(yīng)的學(xué)生进萄,這就是 Unicode 字符集,就像它的名字都表示的锐峭,這是一種所有字符的字符集中鼠。

但是這樣又引出了一系列問題,首先 Unicode 作為一個(gè)獨(dú)立的機(jī)構(gòu)沿癞,希望能推動(dòng)全球文字編碼和字符集標(biāo)準(zhǔn)都統(tǒng)一援雇,但又不能廢除各地方性的編碼方案。Unicode 選擇創(chuàng)建了一套完全獨(dú)立標(biāo)記方式——Unicode scalar values椎扬,這個(gè)方案顯示與我們常見 ASCII 等內(nèi)碼數(shù)值方案完全不同惫搏,然后為了兼容其他主流方案,Unicode 推出了 Unicode 轉(zhuǎn)換格式(Unicode Transformation Format蚕涤,簡(jiǎn)稱為 UTF)筐赔,常見的有 UTF-8、UTF-16 和 UTF-32揖铜。其中 32 是一個(gè)固定四字節(jié)的編碼方案茴丰,他的碼點(diǎn)與 Unicode scalar values 是一一對(duì)應(yīng)的,比較漂亮天吓;16 是由雙字節(jié)和四字節(jié)切換的方案贿肩;8 是變長(zhǎng)的,單字節(jié)時(shí)兼容 ASCII龄寞。再者早期 Unicode 其實(shí)并沒有想到會(huì)進(jìn)來這么多的字符汰规,比如 ??????(家庭)這個(gè) emoji,由于種種原因人們不能滿足只由一個(gè)男人+女人+女孩/男孩這種形式的家庭物邑,不得不繼續(xù)加上 ??????(女人溜哮、女人、男孩)拂封,??????(女人茬射、女人鹦蠕、女孩)冒签,????????(女人、女人钟病、女孩萧恕、男孩)刚梭,???????? 家庭 (男人、男人票唆、女孩朴读、男孩),???????? 家庭 (男人走趋、男人衅金、女孩、女孩)……后來膚色也不能固定為白人簿煌,還得有黃種人氮唯,黑人,外星人之類的姨伟。

出于經(jīng)濟(jì)(能用 ASCII 表示的英文用 UTF-32 固定 4 字節(jié)的方案會(huì)占用額外的空間)和發(fā)展(當(dāng)然可能四字節(jié)也不一定能裝下越來越多的 Unicode 字符)的角度惩琉,UTF-8 目前成為了使用最廣的一種 Unicode 編碼方式。

UTF-8 規(guī)則

UTF-8 的編碼規(guī)則很簡(jiǎn)單夺荒,只有二條:

  1. 對(duì)于單字節(jié)的符號(hào)瞒渠,字節(jié)的第一位設(shè)為0,后面 7 位為這個(gè)符號(hào)的碼點(diǎn)技扼。因此對(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)的碼點(diǎn)。

Unicode 和 UTF-8 之間的轉(zhuǎn)換關(guān)系表 ( x 字符表示碼點(diǎn)占據(jù)的位 )

碼點(diǎn)的位數(shù) 碼點(diǎn)起值 碼點(diǎn)終值 字節(jié)序列 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

需要注意的問題

  1. 中文在 UTF-8 中并不一定長(zhǎng)三個(gè)字節(jié)

    筆者經(jīng)常會(huì)看到一些經(jīng)驗(yàn)豐富的程序員會(huì)認(rèn)為一個(gè)中文字符在 GBK 中是兩個(gè)字節(jié)魔招,轉(zhuǎn)為 UTF-8 是三個(gè)字節(jié)晰搀。所以 UTF-8 中中文字符的長(zhǎng)度是三個(gè)字節(jié),實(shí)際上并不然办斑,需要看這個(gè)這個(gè)字符是不是在 Unicode 的基本面上外恕,非常見字可能會(huì)占 4 個(gè)字節(jié)(UTF-8 可能有 1~4 個(gè)字節(jié)),因?yàn)?GBK 標(biāo)準(zhǔn)提出的時(shí)間早乡翅,所以基本上都在 Unicode 的基本面上鳞疲。

  2. 計(jì)算 UTF-8 編碼的字符串長(zhǎng)度不要想當(dāng)然

    由于 Cocos2d-x 原生并沒有提供計(jì)算 UTF-8 的 API,筆者見過很多奇思妙想的方式計(jì)算中英混合字符串長(zhǎng)度的方式蠕蚜。例如假設(shè)中英混合字符串的每個(gè)字符都占四個(gè)字節(jié)尚洽;調(diào)用原生 OC、Java 庫(kù)函數(shù) String 來計(jì)算長(zhǎng)度等靶累。但是得到長(zhǎng)度后可能需要截取字符串腺毫,截取多長(zhǎng)的參數(shù)又拿不準(zhǔn)癣疟。而且目前主流手機(jī)都支持輸入 emoji,當(dāng)玩家輸入的文字中有大量 emoji 時(shí)截取的效果就可能非常的不理想潮酒。

計(jì)算 UTF-8 編碼字符串長(zhǎng)度的實(shí)例

#include <iostream>

static inline size_t utf8Length(const char *s)
{
  size_t i = 0, j = 0;
  while (s[i])
  {
    //if ((s[i] & 0b11000000) != 0b10000000) j++;
    if ((s[i] & 0xc0) != 0x80)
      j++;
    i++;
  }
  return j;
}

int main()
{
  const auto &utf8 =
      u8"蒼天有井獨(dú)自空睛挚,松柏孤島唯賞楓。武園枯藤空留蘭急黎,星落天川遙映瞳扎狱。";
  auto size = utf8Length(utf8);
  std::cout << size << std::endl;
  return 0;
}
32

Process finished with exit code 0
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市勃教,隨后出現(xiàn)的幾起案子委乌,更是在濱河造成了極大的恐慌,老刑警劉巖荣回,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遭贸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡心软,警方通過查閱死者的電腦和手機(jī)壕吹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來删铃,“玉大人耳贬,你說我怎么就攤上這事×匝洌” “怎么了咒劲?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)诫隅。 經(jīng)常有香客問我棱烂,道長(zhǎng)浪腐,這世上最難降的妖魔是什么鸟款? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任矢沿,我火速辦了婚禮,結(jié)果婚禮上豁生,老公的妹妹穿的比我還像新娘兔毒。我一直安慰自己,他們只是感情好甸箱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布育叁。 她就那樣靜靜地躺著,像睡著了一般芍殖。 火紅的嫁衣襯著肌膚如雪豪嗽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音昵骤,去河邊找鬼。 笑死肯适,一個(gè)胖子當(dāng)著我的面吹牛变秦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播框舔,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼蹦玫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了刘绣?” 一聲冷哼從身側(cè)響起樱溉,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纬凤,沒想到半個(gè)月后福贞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡停士,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年挖帘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恋技。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拇舀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜻底,到底是詐尸還是另有隱情骄崩,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布薄辅,位于F島的核電站要拂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏站楚。R本人自食惡果不足惜宇弛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望源请。 院中可真熱鬧枪芒,春花似錦、人聲如沸谁尸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽良蛮。三九已至抽碌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背货徙。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工左权, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痴颊。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓赏迟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蠢棱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锌杀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容