日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符编码的概念(UTF-8、UTF-16、UTF-32都是什么鬼)

發布時間:2023/12/8 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符编码的概念(UTF-8、UTF-16、UTF-32都是什么鬼) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

?

這就拋出了一個問題,如何才能將字符編號放入內存中呢?

?

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

?

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

?

這兩種方案各有優缺點,請讀者看下面的分析。

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

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

一種方案是為每個字符分配固定長度的內存,并且這塊內存要足夠大,可以容納下所有的字符編號。這種方案最簡單,直接將字符編號放入內存中即可,不需要任何轉換,并且以后在字符串中定位字符、修改字符都非常容易。

字符串就是一串連續的字符序列,它們在內存中按次序挨著存放。在C語言中,字符串由雙引號" "包圍起來。

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

?

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

?

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

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

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

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

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

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

?

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

?

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

?

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

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

?

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

?

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

?

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

?

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

?

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

?

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

?

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

?

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)來存儲字符。

1) UTF-8

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

?

具體的表現形式為:

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

?

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

?

下面是一些字符的編碼實例(綠色部分表示本來的 Unicode 編號):

?

字符N??Unicode 編號(二進制)Unicode 編號(十六進制)UTF-8 編碼(二進制)UTF-8 編碼(十六進制)
010011101110011000101110 11101100
4EE62E EC
0100111011000011?1010011011100010?10111011?10101100
4EC3 A6E2 BB AC

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

2) UTF-32

UTF-32 是固定長度的編碼,始終占用 4 個字節,足以容納所有的 Unicode 字符,所以直接存儲 Unicode 編號即可,不需要任何編碼轉換。浪費了空間,提高了效率。

3) UTF-16

UFT-16 比較奇葩,它使用 2 個或者 4 個字節來存儲。

?

對于 Unicode 編號范圍在 0 ~ FFFF 之間的字符,UTF-16 使用兩個字節存儲,并且直接存儲 Unicode 編號,不用進行編碼轉換,這跟 UTF-32 非常類似。

?

對于 Unicode 編號范圍在 10000~10FFFF 之間的字符,UTF-16 使用四個字節存儲,具體來說就是:將字符編號的所有比特位分成兩部分,較高的一些比特位用一個值介于 D800~DBFF 之間的雙字節存儲,較低的一些比特位(剩下的比特位)用一個值介于 DC00~DFFF 之間的雙字節存儲。

?

如果你不理解什么意思,請看下面的表格:

?

Unicode 編號范圍
(十六進制)具體的 Unicode 編號
(二進制)UTF-16 編碼編碼后的
字節數
0000 0000 ~ 0000 FFFFxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx2
0001 0000---0010 FFFFyyyy yyyy yyxx xxxx xxxx110110yy yyyyyyyy 110111xx xxxxxxxx4

?

位于 D800~0xDFFF 之間的 Unicode 編碼是特別為四字節的 UTF-16 編碼預留的,所以不應該在這個范圍內指定任何字符。如果你真的去查看 Unicode 字符集,會發現這個區間內確實沒有收錄任何字符。

?

?

UTF-16 要求在制定 Unicode 字符集時必須考慮到編碼問題,所以真正的 Unicode 字符集也不是隨意編排字符的。

總結

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

?

如果你希望查看完整的 Unicode 字符集,以及各種編碼方式,請猛擊:https://unicode-table.com/cn/

雖然這個網站有時候無法訪問,但它是最好的一個查看 Unicode 字符集的網站。

GB2312、Shift-JIS 等國家(地區)字符集怎么編碼

GB2312、GBK、Shift-JIS 等特定國家的字符集都是在 ASCII 的基礎上發展起來的,它們都兼容 ASCII,所以只能采用變長的編碼方案:用一個字節存儲 ASCII 字符,用多個字節存儲本國字符。

?

以 GB2312 為例,該字符集收錄的字符較少,所以使用 1~2 個字節編碼。

  • 對于 ASCII 字符,使用一個字節存儲,并且該字節的最高位是 0;
  • 對于中國的字符,使用兩個字節存儲,并且規定每個字節的最高位都是 1。

?

?

由于單字節和雙字節的最高位不一樣,所以很容易區分一個字符到底用了幾個字節。

寬字符和窄字符(多字節字符)

有的編碼方式采用 1~n 個字節存儲,是變長的,例如 UTF-8、GB2312、GBK 等;如果一個字符使用了這種編碼方式,我們就將它稱為多字節字符,或者窄字符。

?

有的編碼方式是固定長度的,不管字符編號大小,始終采用 n 個字節存儲,例如 UTF-32、UTF-16 等;如果一個字符使用了這種編碼方式,我們就將它稱為寬字符。

?

Unicode 字符集可以使用窄字符的方式存儲,也可以使用寬字符的方式存儲;GB2312、GBK、Shift-JIS 等國家編碼一般都使用窄字符的方式存儲;ASCII 只有一個字節,無所謂窄字符和寬字符。

總結

以上是生活随笔為你收集整理的字符编码的概念(UTF-8、UTF-16、UTF-32都是什么鬼)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。