UTF-8 字元集基礎

來源:互聯網
上載者:User

UTF-8 字元集基礎

字元集簡史

在所有字元集中,最知名可能要數被稱為ASCII的7位字元集了。它是美國資訊交換標準委員會(American Standards Committee for Information Interchange)的縮寫, 為美國英語通訊所設計。它由128個字元組成,包括大小寫字母、數字0-9、標點符號、非列印字元(分行符號、定位字元等4個)以及控制字元(退格、響鈴等)組成。

但是,由於他是針對英語設計的,當處理帶有音調標號(形如漢語的拼音)的歐洲文字時就會出現問題。因此,建立出了一些包括255個字元的由ASCII擴充的字元集。其中有一種通常被成為IBM字元集,它把值為128-255之間的字元用於畫圖和畫線,以及一些特殊的歐洲字元。另一種8位字元集是ISO 8859-1 Latin 1,也簡稱為ISO Latin-1。它把位於128-255之間的字元用於拉丁字母表中特殊語言字元的編碼,也因此而得名。

歐洲語言不是地球上的唯一語言,因此亞洲和非洲語言並不能被8位字元集所支援。僅漢語(或pictograms)字母表就有80000以上個字元。但是把漢語、日語和越南語的一些相似的字元結合起來,在不同的語言裡,使不同的字元代表不同的字,這樣只用2個位元組就可以編碼地球上幾乎所有地區的文字。因此,建立了UNICODE編碼。它通過增加一個高位元組對ISO Latin-1字元集進行擴充,當這些高位元組位為0時,低位元組就是ISO Latin-1字元。UNICODE支援歐洲、非洲、中東、亞洲(包括統一標準的東亞像形漢字和韓國像形文字)。但是,UNICODE並沒有提供對諸如Braille, Cherokee, Ethiopic, Khmer, Mongolian, Hmong, Tai Lu, Tai Mau文字的支援。同時它也不支援如Ahom, Akkadian, Aramaic, Babylonian Cuneiform, Balti, Brahmi, Etruscan, Hittite, Javanese, Numidian, Old Persian Cuneiform, Syrian之類的古老的文字。

事實證明,對可以用ASCII表示的字元使用UNICODE並不高效,因為UNICODE比ASCII佔用大一倍的空間,而對ASCII來說高位元組的0對他毫無用處。為瞭解決這個問題,就出現了一些中間格式的字元集,他們被稱為通用轉換格式,既UTF(Universal Transformation Format)。目前存在的UTF格式有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32。本文討論UTF-8字元集的基礎。

UTF_8字元集

UTF-8是UNICODE的一種變長字元編碼,由Ken Thompson於1992年建立。現在已經標準化為RFC 3629。UTF-8用1到6個位元組編碼UNICODE字元。如果UNICODE字元由2個位元組表示,則編碼成UTF-8很可能需要3個位元組,而如果UNICODE字元由4個位元組表示,則編碼成UTF-8可能需要6個位元組。用4個或6個位元組去編碼一個UNICODE字元可能太多了,但很少會遇到那樣的UNICODE字元。

UFT-8轉換表表示如下:

UNICODE UTF-8 
00000000 - 0000007F 0xxxxxxx 
00000080 - 000007FF 110xxxxx 10xxxxxx 
00000800 - 0000FFFF 1110xxxx 10xxxxxx 10xxxxxx 
00010000 - 001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 

實際表示ASCII字元的UNICODE字元,將會編碼成1個位元組,並且UTF-8表示與ASCII字元表示是一樣的。所有其他的UNCODE字元轉化成UTF-8將需要至少2個位元組。每個位元組由一個換碼序列開始。第一個位元組由唯一的換碼序列,由n位1加一位0組成。n位1表示字元編碼所需的位元組數。

樣本

UNICODE uCA(11001010) 編碼成UTF-8將需要2個位元組:

uCA -> C3 8A

1100 1010
110xxxxx 10xxxxxx

1100 1010 -> 110xxxxx 10xxxxxx
-> 110xxxxx 10xxxxx0
-> 110xxxxx 10xxxx10
-> 110xxxxx 10xxx010
-> 110xxxxx 10xx1010
-> 110xxxxx 10x01010
-> 110xxxxx 10001010
-> 110xxxx1 10001010
-> 110xxx11 10001010
-> 11000011 10001010
-> C3 8A

UNICODE uF03F (11110000 00111111) 編碼成UTF-8將需要3個位元組:

u F03F -> EF 80 BF

1111 0000 0011 1111 -> 1110xxxx 10xxxxxx 10xxxxxx
-> 11101111 10000000 10111111
-> EF 80 BF

譯者註:由上分析可以看到,UNCODE到UTF-8的轉換就是先確定編碼所需要的位元組數,然後用UNICODE編碼位從低位到高位依次填入上面表示為x的位上,不足的高位以0補充。以上是個人經驗,如有錯誤,請不惜指教,謝過先:)

UTF-8編碼的優點:

UTF-8編碼可以通過屏蔽位和移位操作快速讀寫。
字串比較時strcmp()和wcscmp()的返回結果相同,因此使排序變得更加容易。
位元組FF和FE在UTF-8編碼中永遠不會出現,因此他們可以用來表明UTF-16或UTF-32文本(見BOM)
UTF-8 是位元組順序無關的。它的位元組順序在所有系統中都是一樣的,因此它實際上並不需要BOM。

UTF-8編碼的缺點:

你無法從UNICODE字元數判斷出UTF-8文本的位元組數,因為UTF-8是一種變長編碼
它需要用2個位元組編碼那些用擴充ASCII字元集只需1個位元組的字元
ISO Latin-1 是UNICODE的子集,但不是UTF-8的子集
8位字元的UTF-8編碼會被email網關過濾,因為internet資訊最初設計為7為ASCII碼。因此產生了UTF-7編碼。
UTF-8 在它的表示中使用值100xxxxx的幾率超過50%, 而現存的實現如ISO 2022, 4873, 6429, 和8859系統,會把它錯認為是C1 控制碼。因此產生了UTF-7.5編碼。

修正的UTF-8:

java使用UTF-16表示內部文本,並支援用於字串序列化的非標準的修正UTF-8編碼。標準UTF-8和修正的UTF-8有兩點不同:
修正的UTF-8中,null字元編碼成2個位元組(11000000 00000000) 而不是標準的1個位元組(00000000),這樣作可以保證編碼後的字串中不會嵌入null字元。因此如果在類C語言中處理字串,文本不會在第一個null字元時截斷(C字串以null結尾)。
在標準UTF-8編碼中,超出基本多語言範圍(BMP - Basic Multilingual Plain)的字元被編碼為4位元組格式,但是在修正的UTF-8編碼中,他們由代理編碼對(surrogate pairs)表示,然後這些代理編碼對在序列中分別重新編碼。結果標準UTF-8編碼中需要4個位元組的字元,在修正後的UTF-8編碼中將需要6個位元組。

位序標誌BOM

BOM(Byte Order Mark)是一個字元,它表明UNICODE文本的UTF-16,UTF-32的編碼位元組順序(高位元組低位元組順序)和編碼方式(UTF-8,UTF-16,UTF-32, 其中UTF-8編碼是位元組順序無關的)。

如下所示:

Encoding Representation 
UTF-8 EF BB BF 
UTF-16 Big Endian FE FF 
UTF-16 Little Endian FF FE 
UTF-32 Big Endian 00 00 FE FF 
UTF-32 Little Endian FF FE 00 00 

UTF-8 C++ 程式編碼樣本:

下面是四個C++函數,他們分別實現2位元組和4位元組UNICODE和UTF-8之間的轉換。

#define MASKBITS 0x3F
#define MASKBYTE 0x80
#define MASK2BYTES 0xC0
#define MASK3BYTES 0xE0
#define MASK4BYTES 0xF0
#define MASK5BYTES 0xF8
#define MASK6BYTES 0xFC

typedef unsigned short Unicode2Bytes;
typedef unsigned int Unicode4Bytes;

void UTF8Encode2BytesUnicode(std::vector< Unicode2Bytes > input,
std::vector< byte >& output)
{
 for(int i=0; i < input.size(); i++)
 {
  // 0xxxxxxx
  if(input[i] < 0x80)
  {
   output.push_back((byte)input[i]);
  }
  // 110xxxxx 10xxxxxx
  else if(input[i] < 0x800)
  {
   output.push_back((byte)(MASK2BYTES | input[i] >> 6));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
  // 1110xxxx 10xxxxxx 10xxxxxx
  else if(input[i] < 0x10000)
  {
   output.push_back((byte)(MASK3BYTES | input[i] >> 12));
   output.push_back((byte)(MASKBYTE | input[i] >> 6 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
 }
}

void UTF8Decode2BytesUnicode(std::vector< byte > input,
std::vector< Unicode2Bytes >& output)
{
 for(int i=0; i < input.size();)
 {
  Unicode2Bytes ch;

  // 1110xxxx 10xxxxxx 10xxxxxx
  if((input[i] & MASK3BYTES) == MASK3BYTES)
  {
   ch = ((input[i] & 0x0F) << 12) | (
   (input[i+1] & MASKBITS) << 6)
   | (input[i+2] & MASKBITS);
   i += 3;
  }
  // 110xxxxx 10xxxxxx
  else if((input[i] & MASK2BYTES) == MASK2BYTES)
  {
   ch = ((input[i] & 0x1F) << 6) | (input[i+1] & MASKBITS);
   i += 2;
  }
  // 0xxxxxxx
  else if(input[i] < MASKBYTE)
  {
   ch = input[i];
   i += 1;
  }
  output.push_back(ch);
 }
}

void UTF8Encode4BytesUnicode(std::vector< Unicode4Bytes > input,
std::vector< byte >& output)
{
 for(int i=0; i < input.size(); i++)
 {
  // 0xxxxxxx
  if(input[i] < 0x80)
  {
   output.push_back((byte)input[i]);
  }
  // 110xxxxx 10xxxxxx
  else if(input[i] < 0x800)
  {
   output.push_back((byte)(MASK2BYTES | input[i] > 6));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
  // 1110xxxx 10xxxxxx 10xxxxxx
  else if(input[i] < 0x10000)
  {
   output.push_back((byte)(MASK3BYTES | input[i] >> 12));
   output.push_back((byte)(MASKBYTE | input[i] >> 6 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
  // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  else if(input[i] < 0x200000)
  {
   output.push_back((byte)(MASK4BYTES | input[i] >> 18));
   output.push_back((byte)(MASKBYTE | input[i] >> 12 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] >> 6 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
  // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  else if(input[i] < 0x4000000)
  {
   output.push_back((byte)(MASK5BYTES | input[i] >> 24));
   output.push_back((byte)(MASKBYTE | input[i] >> 18 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] >> 12 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] >> 6 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
  // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  else if(input[i] < 0x8000000)
  {
   output.push_back((byte)(MASK6BYTES | input[i] >> 30));
   output.push_back((byte)(MASKBYTE | input[i] >> 18 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] >> 12 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] >> 6 & MASKBITS));
   output.push_back((byte)(MASKBYTE | input[i] & MASKBITS));
  }
 }
}

void UTF8Decode4BytesUnicode(std::vector< byte > input,
std::vector< Unicode4Bytes >& output)
{
 for(int i=0; i < input.size();)
 {
  Unicode4Bytes ch;

  // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  if((input[i] & MASK6BYTES) == MASK6BYTES)
  {
   ch = ((input[i] & 0x01) << 30) | ((input[i+1] & MASKBITS) << 24)
   | ((input[i+2] & MASKBITS) << 18) | ((input[i+3]
   & MASKBITS) << 12)
   | ((input[i+4] & MASKBITS) << 6) | (input[i+5] & MASKBITS);
   i += 6;
  }
  // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  else if((input[i] & MASK5BYTES) == MASK5BYTES)
  {
   ch = ((input[i] & 0x03) << 24) | ((input[i+1]
   & MASKBITS) << 18)
   | ((input[i+2] & MASKBITS) << 12) | ((input[i+3]
   & MASKBITS) << 6)
   | (input[i+4] & MASKBITS);
   i += 5;
  }
  // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  else if((input[i] & MASK4BYTES) == MASK4BYTES)
  {
   ch = ((input[i] & 0x07) << 18) | ((input[i+1]
   & MASKBITS) << 12)
   | ((input[i+2] & MASKBITS) << 6) | (input[i+3] & MASKBITS);
   i += 4;
  }
  // 1110xxxx 10xxxxxx 10xxxxxx
  else if((input[i] & MASK3BYTES) == MASK3BYTES)
  {
   ch = ((input[i] & 0x0F) << 12) | ((input[i+1] & MASKBITS) << 6)
   | (input[i+2] & MASKBITS);
   i += 3;
  }
  // 110xxxxx 10xxxxxx
  else if((input[i] & MASK2BYTES) == MASK2BYTES)
  {
   ch = ((input[i] & 0x1F) << 6) | (input[i+1] & MASKBITS);
   i += 2;
  }
  // 0xxxxxxx
  else if(input[i] < MASKBYTE)
  {
   ch = input[i];
   i += 1;
  }
  output.push_back(ch);
 }
}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.