Unicode和UTF-8互相轉換

來自專欄知也無涯4 人贊了文章

很久沒有寫blog,現在工作穩定了,慢慢恢復blog更新,繼續記錄自己的學習歷程。

這兩天工作中遇到了亂碼問題,在解決問題的過程中為了debug,手動轉換了一下編碼。大家都知道用decodeencode ,解決編碼問題。但是很人知道其中的原理,不知不覺間當了一回API Caller。所以特此記錄一下這兩天學習的Unicode和UTF-8的知識點。

UTF是UCS標準(Universal Character Set)和Unicode碼在計算機中的編碼格式,用4個位元組或者2個位元組來表示一個字元,即UTF32(UCS-4)、UTF-16(UCS-2)。由於大部分常用字元高位都是0,只需要16位就能表示(常用字元都位於BMP)。為了節約空間就有了UTF-16,用2個位元組表示一個字元,所以UTF-16是UTF-32的子集。大家常說的形如uu4f60的Unicode,編碼格式其實是UTF-16。

但是UTF-32和UTF-16有一個很嚴重的問題——和C語言不兼容。因為C語言中0000 0000表示字元串結尾,而UTF-32和UTF-16中有很多字元高位都是0,和字元串結尾衝突了。此時,UNIX之父Ken Thompson提出的UTF-8編碼完美解決了這個問題。所以UTF-8和UTF-32、UTF-16相同,也是Unicode的一種編碼格式。我們前面說的Unicode和UTF-8轉換,其實不準確。準確地說是UTF-32、UTF16轉換為UTF-8。

想要轉換UTF-8,必須要先明白UTF-8是如何表示Unicode碼的。UTF-8的編碼表如下:

U-00000000 – U-0000007F: 0xxxxxxx

U-00000080 – U-000007FF: 110xxxxx 10xxxxxxU-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx

U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxxU-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

上面是UTF-8的編碼規則,規則主要分為兩部分。

  1. 如果Unicode碼小於等於127,即ASCII碼。則仍使用ASCII的編碼格式,最高位為0,其餘7位表示0-127。
  2. 如果,Unicode碼大於127,以大端表示。然後從低位開始,每次取6位,加上高位的10,組成一個位元組,直到不足6位。假設前面一共組成了n個位元組,那麼把前n+1位都標記為1,其餘位標記為0。然後加上不足的6位,組成UTF-8編碼的頭部位元組。最後把這些位元組從低位到高位,拼成一串編碼。

這樣描述有點太難以理解了,我們下面逐條分析。


1. ASCII碼的編碼格式

如果是ASCII,則不改變編碼方式,直接用ASCII的編碼,這樣主要是為了向後兼容。例如ASCII碼中的大寫字母「S」,是第83個字元,編碼為0101 0011,0x53,一個位元組表示。在UTF-8中保持不變,還是0101 0011,uu53,還是一個位元組。

2. 非ASCII碼的編碼格式

我們按照上面的規則一步步來編碼,把「習」字轉換成UTF-8編碼。

  • 如果,Unicode碼大於127,以大端表示。
    • 「習」的UTF-16編碼為「u4E60」,以大端表示為??0100 1110 0110 0000?。
  • 從低位開始,每次取6位,加上高位的10,組成一個位元組,直到不足6位。
    • 取6位10 0000,再加上高位的10,組成一個位元組1010 0000
    • 再取6位1110 01,再加上高位的10,組成一個位元組1011 1001
  • 0100,不足6位,前面一共組成了2個位元組,n=2。把前n+1都標記為1,其餘位標記為0,即1110 0000。然後加上不足的6位,組成UTF-8編碼的頭部位元組,即1110 0000 + 0100 = 1110 0100
  • 最後把這些位元組從低位到高位,拼成一串編碼,1110 0100 1011 1001 1010 0000。即E4B9A0,通常表示為「xE4xB9xA0」。

3. 小姿勢:錕斤拷是怎麼出現的?

設備在顯示字元的時候,會把不能顯示的字元顯示為符號?,即「ufffd」。

是的,終端顯示字元的時候,還有一個顯示字符集,這裡就不展開了。

「ufffd」用UTF-8編碼是多少呢,EFBFBD,用三個位元組表示。UTF-8編碼是可變長度的,但是我們的GBK編碼是定長,2個位元組。當兩個UTF-8編碼的?,即EFBFBD EFBFBD,被當作GBK解碼的時候。就會解碼成3個字元,EFBF BDEF BFBD,在GBK編碼中這三個字元就是大名鼎鼎的「錕斤拷」。

推薦閱讀:

查看原文 >>
相關文章