什么是UTF-8


1)開篇啰嗦

感謝這篇博客,在網上轉悠了好幾天,覺得下面這篇博客我讀起來最最容易理解

https://blog.csdn.net/guxiaonuan/article/details/78678043

 

2)起源

 ASCII碼大家都很熟悉了,事實證明,對可以用ASCII表示的字符使用UNICODE並不高效,因為Unicode比ASCII碼占用大一倍的空間,對ASCII來說高字節的0對他毫無用處。為了解決這個問題,就出現了一些中間格式的字符及,他們被稱為通用轉換格式,即UTF(Unicode Transformation Format)。常見的UTF格式有UTF-7, UTF-7.5, UTF-8,UTF-16, 以及 UTF-32。

 字符集為每個字符分配了一個唯一的編號,通過這個編號就能找到對應的字符。在編程過程中我們經常會使用字符,而使用字符的前提就是把字符放入內存中,毫無疑問,放入內存中的僅僅是字符的編號,而不是真正的字符實體。

 

 

3)如何才能將字符編號放入內存中

對於 ASCII 字符集,這很容易。ASCII 總共包含 128 個字符,用 7 個比特位(Bit)恰好能夠存儲,不過考慮到計算機一般把字節(Byte)作為基本單元,為了操作方便,我們不妨用一個字節(也就是 8 個比特位)來存儲 ASCII。這樣雖然浪費了一個比特位,但是讀寫效率提高了。

但是對於 Unicode,問題就沒有這么簡單了。Unicode 目前已經包含了上百萬的字符,位置靠前的字符用一個字節就能存儲,位置靠后的字符用三個字節才能存儲。我們可以為所有字符都分配三個字節的內存,也可以為編號小的字符分配一個字節或者兩個字節的內存,而為編號大的字符分配三個字節的內存。

 

----字符集和字符編碼不一樣

字符集和字符編碼不是一個概念,字符集定義了文字和二進制的對應關系,為字符分配了唯一的編號,而字符編碼規定了如何將文字的編號存儲到內存中。有的字符集在制定時就考慮到了編碼的問題,是和編碼結合在一起的;有的字符集只管制定字符的編號,至於怎么編碼,是其他人的事情。

 

4)方案1:為每個字符分配固定長度的內存

目前的 Unicode 已經收錄了上百萬的字符,至少需要三個字節才能容納下所有的字符編號。假設字符  'A3中¥'  的 Unicode 編碼值(十六進制形式)分別是 2A、31、DA49、BB672C,那么它們在內存中的存儲形式為:

 

 

在幾乎所有的字符集中,常用字符的編號往往比較小,罕見字符的編號往往比較大,包括 Unicode 在內。

 

’A‘和‘3’ 是 ASCII 編碼中的字符,Unicode 為了兼容 ASCII,在設計時刻意保留了原來 ASCII 中字符的編號,所以英文字母和阿拉伯數字在 Unicode 中的編號都非常小,用一個字節足以容納。可以看做是一個極其少見,或者只有極少數地區才會使用到的字符,這樣的字符編號往往比較大,有時候需要三個字節才能容納。

是人民幣符號,是漢字文化的一部分,它和其它漢字一樣,實際上是用兩個字節存儲的,不過這里我們為了演示,故意犯錯地說它需要三個字節。

 

上圖中帶灰色背景的字節是沒有用到的字節,它們就是被浪費掉的一部分內存空間,這就是用固定長度的內存來存儲字符編號的缺點:常用字符的編號都比較小,這種方案會浪費很多內存空間,對於以英文為主的國家,比如美國、加拿大、英國等,內存利用率甚至會低於 50%。

 

5)方案2:為每個字符分配盡量少的內存

既然上面的方案有缺點,那我們就來改進一下。改進的思路也很明確,就是把空閑的內存壓縮掉,為每個字符分配盡量少的字節,例如, 'A' 和 '3' 分配一個字節足以, '中'分配兩個字節足以

這樣雖然沒有了空閑字節,不浪費任何內存空間了,但是又出現新的問題了:如果我不說,怎么知道’2A‘表示一個字符,而不是'2A31' 或者’2A31DA‘才表示一個字符?后面的字符也有類似的問題。

 

對於第一種方案,每個字符占用的字節數是固定的,很容易區分各個字符;而這種方案,不同的字符占用的字節數不同,字符之間也沒有特殊的標記,計算機是無法定位字符的。

 這種方案還需要改進,必須讓不同的字符編碼有不同的特征,並且字符處理程序也需要調整,要根據這些特征去識別不同的字符。

要想讓不同的字符編碼有不同的特征,可以從兩個方面下手:

1) 一是從字符集本身下手,在設計字符集時,刻意讓不同的字符編號有不同的特征。

 

例如,對於編號較小的、用一個字節足以容納的字符,我們就可以規定這個字符編號的最高位(Bit)必須是 0;對於編號較大的、要用兩個字節存儲的字符,我們就可以規定這個字符編號的高字節的最高位必須是 1,低字節的最高位必須是 0;對於編號更大的、需要三個字節存儲的字符,我們就可以規定這個字符編號的所有字節的最高位都必須是 1。

 

程序在定位字符時,從前往后依次掃描,如果發現當前字節的最高位是 0,那么就把這一個字節作為一個字符編號。如果發現當前字節的最高位是 1,那么就繼續往后掃描,如果后續字節的最高位是 0,那么就把這兩個字節作為一個字符編號;如果后續字節的最高位是 1,那么就把挨着的三個字節作為一個字符編號。

 

這種方案的缺點很明顯,它會導致字符集不連續,中間留出大量空白區域,這些空白區域不能定義任何字符。

 

2) 二是從字符編號下手,可以設計一種轉換方案,字符編號在存儲之前先轉換為有特征的、容易定位的編號,讀取時再按照相反的過程轉換成字符本來的編號。

 

那么,轉換后的編號要具備什么樣的特征呢?其實也可以像上面一樣,根據字節的最高位是 0 還是 1 來判斷字符到底占用了幾個字節。

 

相比第一種方案,這種方案有缺點也有優點:

  • 缺點就是多了轉換過程,字符在存儲和讀取時要經過轉換,效率低;
  • 優點就是在制定字符集時不用考慮存儲的問題,可以任意排布字符。

6)Unicode 到底使用哪種編碼方案

Unicode 是一個獨立的字符集,它並不是和編碼綁定的,你可以采用第一種方案,為每個字符分配固定長度的內存,也可以采用第二種方案,為每個字符分配盡量少的內存。

 

需要注意的是,Unicode 只是一個字符集,在制定的時候並沒有考慮編碼的問題,所以采用第二種方案時,就不能從字符集本身下手了,只能從字符編號下手,這樣在存儲和讀取時都要進行適當的轉換。

 

Unicode 可以使用的編碼有三種,分別是:

  • UFT-8:一種變長的編碼方案,使用 1~6 個字節來存儲;
  • UFT-32:一種固定長度的編碼方案,不管字符編號大小,始終使用 4 個字節來存儲;
  • UTF-16:介於 UTF-8 和 UTF-32 之間,使用 2 個或者 4 個字節來存儲,長度既固定又可變。

 

UTF 是 Unicode Transformation Format 的縮寫,意思是“Unicode轉換格式”,后面的數字表明至少使用多少個比特位(Bit)來存儲字符。

 

6) UTF-8

UTF-8 的編碼規則很簡單:如果只有一個字節,那么最高的比特位為 0;如果有多個字節,那么第一個字節從最高位開始,連續有幾個比特位的值為 1,就使用幾個字節編碼,剩下的字節均以 10 開頭。

 

具體的表現形式為:

  • 0xxxxxxx:單字節編碼形式,這和 ASCII 編碼完全一樣,因此 UTF-8 是兼容 ASCII 的;
  • 110xxxxx 10xxxxxx:雙字節編碼形式;
  • 1110xxxx 10xxxxxx 10xxxxxx:三字節編碼形式;
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字節編碼形式。

 

xxx 就用來存儲 Unicode 中的字符編號。

對於常用的字符,它的 Unicode 編號范圍是 0 ~ FFFF,用 1~3 個字節足以存儲,只有及其罕見,或者只有少數地區使用的字符才需要 4~6個字節存儲。

 

 只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因為它們沒有單字節編碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM