編碼

阮一峯的網絡日誌 » 首頁 » 檔案

 

分類:

 

字符編碼筆記:ASCII,Unicode 和 UTF-8

作者: 阮一峯

日期: 2007年10月28日

珠峯培訓

今天中午,我突然想搞清楚 Unicode 和 UTF-8 之間的關係,就開始查資料。

這個問題比我想象的複雜,午飯後一直看到晚上9點,纔算初步搞清楚。

下面就是我的筆記,主要用來整理自己的思路。我儘量寫得通俗易懂,希望能對其他朋友有用。畢竟,字符編碼是計算機技術的基石,想要熟練使用計算機,就必須懂得一點字符編碼的知識。

一、ASCII 碼

我們知道,計算機內部,所有信息最終都是一個二進制值。每一個二進制位(bit)有01兩種狀態,因此八個二進制位就可以組合出256種狀態,這被稱爲一個字節(byte)。也就是說,一個字節一共可以用來表示256種不同的狀態,每一個狀態對應一個符號,就是256個符號,從0000000011111111

上個世紀60年代,美國製定了一套字符編碼,對英語字符與二進制位之間的關係,做了統一規定。這被稱爲 ASCII 碼,一直沿用至今。

ASCII 碼一共規定了128個字符的編碼,比如空格SPACE是32(二進制00100000),大寫的字母A是65(二進制01000001)。這128個符號(包括32個不能打印出來的控制符號),只佔用了一個字節的後面7位,最前面的一位統一規定爲0

二、非 ASCII 編碼

英語用128個符號編碼就夠了,但是用來表示其他語言,128個符號是不夠的。比如,在法語中,字母上方有注音符號,它就無法用 ASCII 碼錶示。於是,一些歐洲國家就決定,利用字節中閒置的最高位編入新的符號。比如,法語中的é的編碼爲130(二進制10000010)。這樣一來,這些歐洲國家使用的編碼體系,可以表示最多256個符號。

但是,這裏又出現了新的問題。不同的國家有不同的字母,因此,哪怕它們都使用256個符號的編碼方式,代表的字母卻不一樣。比如,130在法語編碼中代表了é,在希伯來語編碼中卻代表了字母Gimel (ג),在俄語編碼中又會代表另一個符號。但是不管怎樣,所有這些編碼方式中,0--127表示的符號是一樣的,不一樣的只是128--255的這一段。

至於亞洲國家的文字,使用的符號就更多了,漢字就多達10萬左右。一個字節只能表示256種符號,肯定是不夠的,就必須使用多個字節表達一個符號。比如,簡體中文常見的編碼方式是 GB2312,使用兩個字節表示一個漢字,所以理論上最多可以表示 256 x 256 = 65536 個符號。

中文編碼的問題需要專文討論,這篇筆記不涉及。這裏只指出,雖然都是用多個字節表示一個符號,但是GB類的漢字編碼與後文的 Unicode 和 UTF-8 是毫無關係的。

三. Unicode

正如上一節所說,世界上存在着多種編碼方式,同一個二進制數字可以被解釋成不同的符號。因此,要想打開一個文本文件,就必須知道它的編碼方式,否則用錯誤的編碼方式解讀,就會出現亂碼。爲什麼電子郵件常常出現亂碼?就是因爲發信人和收信人使用的編碼方式不一樣。

可以想象,如果有一種編碼,將世界上所有的符號都納入其中。每一個符號都給予一個獨一無二的編碼,那麼亂碼問題就會消失。這就是 Unicode,就像它的名字都表示的,這是一種所有符號的編碼。

Unicode 當然是一個很大的集合,現在的規模可以容納100多萬個符號。每個符號的編碼都不一樣,比如,U+0639表示阿拉伯字母AinU+0041表示英語的大寫字母AU+4E25表示漢字。具體的符號對應表,可以查詢unicode.org,或者專門的漢字對應表

四、Unicode 的問題

需要注意的是,Unicode 只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。

比如,漢字的 Unicode 是十六進制數4E25,轉換成二進制數足足有15位(100111000100101),也就是說,這個符號的表示至少需要2個字節。表示其他更大的符號,可能需要3個字節或者4個字節,甚至更多。

這裏就有兩個嚴重的問題,第一個問題是,如何才能區別 Unicode 和 ASCII ?計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?第二個問題是,我們已經知道,英文字母只用一個字節表示就夠了,如果 Unicode 統一規定,每個符號用三個或四個字節表示,那麼每個英文字母前都必然有二到三個字節是0,這對於存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的。

它們造成的結果是:1)出現了 Unicode 的多種存儲方式,也就是說有許多種不同的二進制格式,可以用來表示 Unicode。2)Unicode 在很長一段時間內無法推廣,直到互聯網的出現。

五、UTF-8

互聯網的普及,強烈要求出現一種統一的編碼方式。UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式。其他實現方式還包括 UTF-16(字符用兩個字節或四個字節表示)和 UTF-32(字符用四個字節表示),不過在互聯網上基本不用。重複一遍,這裏的關係是,UTF-8 是 Unicode 的實現方式之一。

UTF-8 最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。

UTF-8 的編碼規則很簡單,只有二條:

1)對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的 Unicode 碼。因此對於英語字母,UTF-8 編碼和 ASCII 碼是相同的。

2)對於n字節的符號(n > 1),第一個字節的前n位都設爲1,第n + 1位設爲0,後面字節的前兩位一律設爲10。剩下的沒有提及的二進制位,全部爲這個符號的 Unicode 碼。

下表總結了編碼規則,字母x表示可用編碼的位。

Unicode符號範圍     |        UTF-8編碼方式
(十六進制)        |              (二進制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟據上表,解讀 UTF-8 編碼非常簡單。如果一個字節的第一位是0,則這個字節單獨就是一個字符;如果第一位是1,則連續有多少個1,就表示當前字符佔用多少個字節。

下面,還是以漢字爲例,演示如何實現 UTF-8 編碼。

的 Unicode 是4E25100111000100101),根據上表,可以發現4E25處在第三行的範圍內(0000 0800 - 0000 FFFF),因此的 UTF-8 編碼需要三個字節,即格式是1110xxxx 10xxxxxx 10xxxxxx。然後,從的最後一個二進制位開始,依次從後向前填入格式中的x,多出的位補0。這樣就得到了,的 UTF-8 編碼是11100100 10111000 10100101,轉換成十六進制就是E4B8A5

六、Unicode 與 UTF-8 之間的轉換

通過上一節的例子,可以看到的 Unicode碼 是4E25,UTF-8 編碼是E4B8A5,兩者是不一樣的。它們之間的轉換可以通過程序實現。

Windows平臺,有一個最簡單的轉化方法,就是使用內置的記事本小程序notepad.exe。打開文件後,點擊文件菜單中的另存爲命令,會跳出一個對話框,在最底部有一個編碼的下拉條。

bg2007102801.jpg

裏面有四個選項:ANSIUnicodeUnicode big endianUTF-8

1)ANSI是默認的編碼方式。對於英文文件是ASCII編碼,對於簡體中文文件是GB2312編碼(只針對 Windows 簡體中文版,如果是繁體中文版會採用 Big5 碼)。

2)Unicode編碼這裏指的是notepad.exe使用的 UCS-2 編碼方式,即直接用兩個字節存入字符的 Unicode 碼,這個選項用的 little endian 格式。

3)Unicode big endian編碼與上一個選項相對應。我在下一節會解釋 little endian 和 big endian 的涵義。

4)UTF-8編碼,也就是上一節談到的編碼方法。

選擇完"編碼方式"後,點擊"保存"按鈕,文件的編碼方式就立刻轉換好了。

七、Little endian 和 Big endian

上一節已經提到,UCS-2 格式可以存儲 Unicode 碼(碼點不超過0xFFFF)。以漢字爲例,Unicode 碼是4E25,需要用兩個字節存儲,一個字節是4E,另一個字節是25。存儲的時候,4E在前,25在後,這就是 Big endian 方式;25在前,4E在後,這是 Little endian 方式。

這兩個古怪的名稱來自英國作家斯威夫特的《格列佛遊記》。在該書中,小人國裏爆發了內戰,戰爭起因是人們爭論,吃雞蛋時究竟是從大頭(Big-endian)敲開還是從小頭(Little-endian)敲開。爲了這件事情,前後爆發了六次戰爭,一個皇帝送了命,另一個皇帝丟了王位。

第一個字節在前,就是"大頭方式"(Big endian),第二個字節在前就是"小頭方式"(Little endian)。

那麼很自然的,就會出現一個問題:計算機怎麼知道某一個文件到底採用哪一種方式編碼?

Unicode 規範定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做"零寬度非換行空格"(zero width no-break space),用FEFF表示。這正好是兩個字節,而且FFFE1

如果一個文本文件的頭兩個字節是FE FF,就表示該文件採用大頭方式;如果頭兩個字節是FF FE,就表示該文件採用小頭方式。

八、實例

下面,舉一個實例。

打開"記事本"程序notepad.exe,新建一個文本文件,內容就是一個字,依次採用ANSIUnicodeUnicode big endianUTF-8編碼方式保存。

然後,用文本編輯軟件UltraEdit 中的"十六進制功能",觀察該文件的內部編碼方式。

1)ANSI:文件的編碼就是兩個字節D1 CF,這正是的 GB2312 編碼,這也暗示 GB2312 是採用大頭方式存儲的。

2)Unicode:編碼是四個字節FF FE 25 4E,其中FF FE表明是小頭方式存儲,真正的編碼是4E25

3)Unicode big endian:編碼是四個字節FE FF 4E 25,其中FE FF表明是大頭方式存儲。

4)UTF-8:編碼是六個字節EF BB BF E4 B8 A5,前三個字節EF BB BF表示這是UTF-8編碼,後三個E4B8A5就是的具體編碼,它的存儲順序與編碼順序是一致的。

九、延伸閱讀

(完)

文檔信息

QCon

騰訊課堂

相關文章

  • 2018.07.16: CAP 定理的含義

    分佈式系統(distributed system)正變得越來越重要,大型網站幾乎都是分佈式的。

  • 2018.05.09: 根域名的知識

    域名是互聯網的基礎設施,只要上網就會用到。

  • 2018.01.21: 彙編語言入門教程

    學習編程其實就是學高級語言,即那些爲人類設計的計算機語言。

  • 2018.01.11: 加密貨幣的本質

    現在,各種加密貨幣(cryptocurrency)不計其數。

廣告(購買廣告位)

優達學城

留言(218條)

Stark 說:

很有意思的內容
講解得通俗易懂,非常感謝您用較長的時間學習,並用精簡的語言概括

2007年10月29日 10:10 | # | 引用

flyisland 說:

本人是計算機從業人員,對文中提到的知識也都基本瞭解,但是像阮兄這樣,花了半天時間就弄清楚來龍去脈,同時講述的如此清楚,實在佩服。

本帖有pmp嫌疑,但實在是不能不p啊 :)。我想,動手寫過技術文章的人都會同意我的。

2007年10月29日 10:52 | # | 引用

只如初見 說:

是啊,我這個超級菜鳥都看明白了,阮兄對技術的理解和說明能力叫人佩服,謝謝分享。

2007年10月29日 11:32 | # | 引用

姬着 說:

文章裏說:UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。

那USC-2編碼方式如何實現超過兩個字節的符號的存儲?謝謝

2007年10月29日 17:04 | # | 引用

大徐 說:

對了解字符編碼很有幫助,謝謝!

2007年10月29日 23:11 | # | 引用

Ruan YiFeng 說:

引用只如初見的發言:
是啊,我這個超級菜鳥都看明白了,阮兄對技術的理解和說明能力叫人佩服,謝謝分享。

“延伸閱讀”中第一個鏈接,纔是真正的通俗易懂,我只是向他學習而已。

2007年10月30日 09:13 | # | 引用

Ruan YiFeng 說:

引用姬着的發言:
文章裏說:UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。 那USC-2編碼方式如何實現超過兩個字節的符號的存儲?謝謝

USC-2只能用存儲兩個字節的Unicode,超過這一範圍的符號,它不能表示。

2007年10月30日 09:15 | # | 引用

Annis 說:


我是Annis,想邀請您參加拼搏到底FeedSky博客挑戰賽,
這是一個很好的宣傳、展示自己的機會,
有興趣您可以自己到相關網站了解具體參賽細節。
http://www.feedsky.com/challenge/?u=141270

2007年10月30日 10:43 | # | 引用

Bill 說:

PS:本文只是主要介紹了UTF-8編碼,下面這篇文章對於GB碼與Big5有更詳細的介紹。

漢字編碼中現在主要用到的有三類,包括GBK,GB2312和Big5。

1、GB2312又稱國標碼,由國家標準總局發佈,1981年5月1日實施,通行於大陸。新加坡等地也使用此編碼。它是一個簡化字的編碼規範,當然也包括其他的符號、字母、日文假名等,共7445個圖形字符,其中漢字佔6763個。我們平時說6768個漢字,實際上裏邊有5個編碼爲空白,所以總共有6763個漢字。

GB2312規定“對任意一個圖形字符都採用兩個字節表示,每個字節均採用七位編碼表示”,習慣上稱第一個字節爲“高字節”,第二個字節爲“低字節”。GB2312中漢字的編碼範圍爲,第一字節0xB0-0xF7(對應十進制爲176-247),第二個字節0xA0-0xFE(對應十進制爲160-254)。

GB2312將代碼表分爲94個區,對應第一字節(0xa1-0xfe);每個區94個位(0xa1-0xfe),對應第二字節,兩個字節的值分別爲區號值和位號值加32(2OH),因此也稱爲區位碼。01-09區爲符號、數字區,16-87區爲漢字區(0xb0-0xf7),10-15區、88-94區是有待進一步標準化的空白區。


2、Big5又稱大五碼,主要爲香港與臺灣使用,即是一個繁體字編碼。每個漢字由兩個字節構成,第一個字節的範圍從0X81-0XFE(即129-255),共126種。第二個字節的範圍不連續,分別爲0X40-0X7E(即64-126),0XA1-0XFE(即161-254),共157種。


3、GBK是GB2312的擴展,是向上兼容的,因此GB2312中的漢字的編碼與GBK中漢字的相同。另外,GBK中還包含繁體字的編碼,它與Big5編碼之間的關係我還沒有弄明白,好像是不一致的。GBK中每個漢字仍然包含兩個字節,第一個字節的範圍是0x81-0xFE(即129-254),第二個字節的範圍是0x40-0xFE(即64-254)。GBK中有碼位23940個,包含漢字21003個。

Bill再推薦一個網站 專門介紹漢字與漢字計算機化的 漢典 http://www.zdic.net/

2007年10月30日 22:26 | # | 引用

簡單 說:

http://www.zdic.net/


很強的網站,收藏

2007年10月31日 09:06 | # | 引用

Ruan YiFeng 說:

引用Bill的發言:
本文只是主要介紹了UTF-8編碼,下面這篇文章對於GB碼與Big5有更詳細的介紹。

這篇網誌中有兩個地方需要補充一下,我忘了在原文中提到。

1.

國際標準化組織通過了一套ISO-8859-1的編碼,規定了單字節256個符號的編碼方式。目前,這是8位編碼的國際標準。

2.

Unicode編碼中表示字節排列順序的那個文件頭,叫做BOM(byte-order mark),FFFE和FEFF就是不同的BOM。

UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字節順序是不變的,因此這個文件頭實際上不起作用。有一些編程語言是ISO-8859-1編碼,所以如果用UTF-8針對這些語言編程序,就必須去掉BOM,即保存成“UTF-8—無BOM”的格式纔可以,PHP語言就是這樣。

2007年10月31日 23:57 | # | 引用

請教 說:

我怎麼用 notepad 保存成 UTF-8 後 
用 UltraEdit 看 還是 FF FE 25 4E啊???

2007年11月24日 01:36 | # | 引用

請教 說:

引用請教的發言:
我怎麼用 notepad 保存成 UTF-8 後 用 UltraEdit 看 還是 FF FE 25 4E啊???

哦 知道了。 是UltraEdit 默認會以 UTF-16的方式打開UTF-8編碼的文件, 要設置或者用Hex Workshop打開就可以看到樓主所說的效果。

2007年11月24日 02:15 | # | 引用

Cloudream 說:

(點擊支持我)

這些字符是違反adsense規定的……

2007年11月27日 19:08 | # | 引用

jackywdx 說:

這麼精彩的講解,實在不得不向作者致敬!
內容講解得非常詳細但又通俗易懂,謝謝了~~

2008年3月10日 10:01 | # | 引用

jinwangen 說:

非常感謝

2008年4月23日 09:22 | # | 引用

LMN 說:

非常感謝,值得一看

2008年5月 4日 13:07 | # | 引用

網友 說:

標題“6. Unicode與UTF-8之間的轉換”不嚴謹。

Unicode是一種字符集,概念比較抽象,你已經提到,它存儲的時候必須用適當的編碼方式。

你的標題的意思,實際上是UTF-16和UTF-8之間的轉換。

至於windows notepad保存對話框的編碼方式列表中列出“Unicode”這一個名字,實際上是一個通俗的稱呼(來自windows NT的早期版本的習慣,一直沿用),指UTF-16,而不是嚴格的稱呼。希望你行文不要受這個影響。

2008年5月 8日 17:15 | # | 引用

Ruan YiFeng 說:

引用網友的發言:

標題“6. Unicode與UTF-8之間的轉換”不嚴謹。

Unicode是一種字符集,概念比較抽象,你已經提到,它存儲的時候必須用適當的編碼方式。

你的標題的意思,實際上是UTF-16和UTF-8之間的轉換。

UTF-16不等於unicode啊,不能混爲一談。

2008年5月 9日 00:33 | # | 引用

Ivan WONG 說:

嗯,看完了長不少知識.

2008年5月12日 11:19 | # | 引用

Michael Zheng 說:

我只能這麼說,看了那麼多的對字符集的解釋

這個絕對是最通俗易懂並且還解決了常見疑問的最好的文章 :)

收藏

2008年5月12日 12:42 | # | 引用

wxs8088 說:

很好的一編文章

2008年5月16日 03:07 | # | 引用

人跟人的差距咋就這麼大呢 說:

好文章!收藏了!

樓主何不把ansi編碼方式也說說!
讓俺這樣的小學新生借樓主的理解能力再學習一把.

2008年5月16日 11:58 | # | 引用

學習了 說:

學習了

2008年6月25日 11:20 | # | 引用

水中魚 說:

引用Ruan YiFeng的發言:

UTF-16不等於unicode啊,不能混爲一談。


UTF-16擴充了Unicode,包括了一些稀有字符,想我們國家的滿文,藏文等等,兩者基本上等價

2008年7月11日 10:44 | # | 引用

heroboy 說:

其實我認爲,編碼其實有2個意思。1.一個是把字符和數字對應起來(比如unicode和GBXXXX等)。2.還有就是相應在數字在計算機中的表示,也就是和字節序列對應起來(比如utf8,mbcs等)。
我有個問題,windows下的mbcs編碼(2)用的編碼(1)是不是GB系列的編碼(1),而unicode編碼(2)用的是unicode編碼(1)

2008年7月31日 15:07 | # | 引用

Demo 說:

拜讀。。。受益匪淺。。長見識了 。。頂樓主

2008年8月15日 01:56 | # | 引用

GoBoat 說:

很精彩啊

2008年8月25日 11:11 | # | 引用

yangxiups 說:

有時總想去搞清楚他們的區別,可總是無果而終。
講的很通俗。收藏了。

2008年8月26日 16:58 | # | 引用

Maxwell 說:

通俗易懂,對樓主的付出表示感謝。
好文章,收藏!

2008年8月26日 18:03 | # | 引用

hao 說:

Big endian 是大頭,還是大尾?

2008年9月 4日 10:18 | # | 引用

gaga 說:

本文充分展現了阮兄收集信息,總結和歸納信息的能力,佩服和學習!

2008年9月 8日 12:02 | # | 引用

BATMAN 說:

非常感謝樓主提供的信息,支持個

2008年9月24日 20:54 | # | 引用

zhangzhen 說:

挺好的,謝謝

2008年10月 3日 11:20 | # | 引用

陳 說:

謝謝

2008年10月10日 21:36 | # | 引用

Texas 說:

非常感謝樓主。

請繼續寫DBCS->UNICODE的文章

2008年10月30日 12:06 | # | 引用

riskman 說:

寫得真不錯!!

2008年10月30日 15:07 | # | 引用

Taken 說:

感謝阮兄精彩講解...收穫良多

另:能否稍微再增加點UTF-16,UTF-32的基本介紹和應用範圍的介紹...

2008年11月 6日 15:41 | # | 引用

syjun37 說:

非常感謝這篇文章,我想請問一下,GBK和BIG5之間的關係是怎樣的呢?是包含還是交集呢?

2008年11月 7日 13:33 | # | 引用

gg 說:

引用syjun37的發言:
非常感謝這篇文章,我想請問一下,GBK和BIG5之間的關係是怎樣的呢?是包含還是交集呢?

完全不同的兩套編碼標準,對應不同的代碼頁。同樣的字在big5和GBK中的編碼是不一樣的。

2008年11月26日 23:04 | # | 引用

Honeyhacker 說:

相當不錯,這是我打算研究編碼的時候看到的第一篇文章。幾天之後再來評論!!

2008年11月28日 08:45 | # | 引用

Honeyhacker 說:

你在文中提到:“UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度”。我在http://dev.csdn.net/develop/article/83/83012.shtm上看到:
以下是Unicode和UTF-8之間的轉換關係表:

U-00000000 - U-0000007F: 0xxxxxxx

U-00000080 - U-000007FF: 110xxxxx 10xxxxxx

U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx

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

U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
該文中明顯說到UTF-8不限於四個字節。
請問得到1-4個字節的結論是從哪裏找到的?

2008年11月28日 10:23 | # | 引用

Ruan YiFeng 說:

引用Honeyhacker的發言:

該文中明顯說到UTF-8不限於四個字節。
請問得到1-4個字節的結論是從哪裏找到的?

早先的規定是,utf-8可以到6個字節。但是,這已經廢除了,目前規定是隻支持4個字節,大於U+001FFFFF的符號utf-8不再支持。具體請參閱wikipedia。

2008年11月28日 11:18 | # | 引用

Honeyhacker 說:

原來如此!感謝你的回覆!再問一個問題,從網絡中抓獲的數據報,在用wireshark分析的時候,如何判斷其中的哪些數據使用了Unicode編碼?(從snort給出的信息中,該條數據確實有Unicode編碼)。

2008年11月28日 21:38 | # | 引用

morpheus 說:

前輩,計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢
這個是怎麼實現的呢,我有點不明白,還望回覆一下,感激不盡。

2008年11月29日 13:31 | # | 引用

Honeyhacker 說:

或者這樣說,兩臺主機通信的時候,A接收到B發送的消息後,它如何知道該報文采取的是什麼編碼類型?在報文的格式裏面好像沒有提到這一點吧!

2008年12月 1日 09:47 | # | 引用

chen3feng 說:

引用Ruan YiFeng的發言:

UTF-16不等於unicode啊,不能混爲一談。

Windows 從 NT 開始,一開始的內碼是 UCS-2,只支持 Unicode BMP 字符,後來做了擴充,目前的內碼就是 UTF-16,通常不嚴格地稱爲 Unicode。

2008年12月 2日 10:15 | # | 引用

Honeyhacker 說:

引用Honeyhacker的發言:
或者這樣說,兩臺主機通信的時候,A接收到B發送的消息後,它如何知道該報文采取的是什麼編碼類型?在報文的格式裏面好像沒有提到這一點吧!

已經弄明白了!不可一概而論!

2008年12月 7日 22:14 | # | 引用

 說:

受益匪淺謝謝老師!

2009年1月16日 16:34 | # | 引用

挪威的森林 說:

好文!是我看過的關於字符/字符集入門文章中最通俗易懂而又有效的一篇!

2009年1月20日 00:08 | # | 引用

wangkai 說:

佩服,不得不頂,一直想搞清楚,今天總算清楚了一點

2009年2月13日 22:17 | # | 引用

wangkai 說:

關於第8點:8. 實例
我照着做了,但是結果卻不是這樣,
後面三種方式顯示的都是 FF FE 25 4E
這是怎麼回事?
我用的是vista系統

2009年2月13日 22:50 | # | 引用

ArthurLee 說:

通俗易懂,受益匪淺,謝謝指教!O(∩_∩)O~

2009年2月23日 20:44 | # | 引用

www 說:

很感謝 寫的很生動 楞看了百度百科半天沒懂。。。

2009年3月 9日 23:10 | # | 引用

xingyifeng 說:

謝謝
你們的討論很精彩
這樣纔會有長進

2009年4月22日 11:56 | # | 引用

手腳冰涼 說:

一直對這些字符集和編碼比較頭疼,今天看了阮兄和大家的討論,終於有點清楚了,謝謝阮兄給大家帶來這麼好的文章。

2009年5月 5日 09:03 | # | 引用

illidan.zen 說:

通俗易懂,我頂!

2009年5月29日 08:41 | # | 引用

sanki 說:

不錯不錯,寫得太棒了!

2009年5月31日 22:48 | # | 引用

cocobear 說:

寫得不錯。。

2009年6月 1日 13:56 | # | 引用

IT民工 說:

>需要注意的是,Unicode只是一個符號集,它只規定了符號的二進制>代碼,卻沒有規定這個二進制代碼應該如何存儲。
>比如,漢字“嚴”的unicode是十六進制數4E25,轉換成二進制數足>足有15位(100111000100101),也就是說這個符號的表示至少需>要2個字節。表示其他更大的符號,可能需要3個字節或者4個字節,甚>至更多。

有一點疑問,unicode編碼使用2個字節也就是16位2進制數字來表示字符,那麼在存儲的時候,爲什麼會超過2個字節以上呢?
unicode所能定義的符號總共有65536個,而且裏面的每一個符號都是用2個字節表示的,包括ascii碼也都是在高位補零的,麻煩樓主解釋一下

2009年6月30日 15:26 | # | 引用

vivi 說:

在實例8中,我這裏ASCII碼是D1 CF。但是其它的存儲方式,打開都是顯示 FF FE 25 4E,不論用什麼方式存儲?

2009年7月 6日 10:45 | # | 引用

jing.lu 說:

非常生動形象,易於理解,很不錯。

2009年8月10日 16:28 | # | 引用

xix 說:

太感謝您的文章了,收藏了。
之前也看過幾篇介紹utf8編碼的,但都是語言生硬晦澀的長篇大論,感覺太專業、太複雜,看着頭大。

這篇文章總結的很好,本來繁雜的東東,理得很清晰易懂。感謝!

2009年9月29日 17:15 | # | 引用

patric 說:

vivi 說:

在實例8中,我這裏ASCII碼是D1 CF。但是其它的存儲方式,打開都是顯示 FF FE 25 4E,不論用什麼方式存儲?


我操作了,和你說的情況一樣。

2009年10月22日 09:19 | # | 引用

2 說:

一知半解,誤人子弟

2009年11月18日 09:20 | # | 引用

美麗明天 說:

我想把網站做成韓文的,不知道有什麼好方法.

2009年11月23日 09:13 | # | 引用

jhx 說:

謝謝了!

2009年11月27日 16:14 | # | 引用

houkai 說:

寫的很通俗 學懂了 謝謝!

2009年12月23日 11:33 | # | 引用

goodappli 說:

第一個參考鏈接太難打開了,半天都沒有載入,讓人崩潰,改天再試。

2009年12月30日 22:31 | # | 引用

flowsands 說:

阮大有空再來聊聊新字符集國標GB18030吧,Superset of gb2312.
參見:http://en.wikipedia.org/wiki/GB_18030

2010年1月 5日 16:24 | # | 引用

XjAcKs 說:

原來是這樣,這下更清楚了。謝謝!

2010年2月12日 09:11 | # | 引用

allen 說:

感謝,寫的很清楚詳細

2010年3月23日 11:10 | # | 引用

htc 說:

寫得太好了,感謝樓主的貢獻

2010年3月25日 15:50 | # | 引用

mlmt 說:

正在頭痛這個編碼的事,感謝樓主。
延伸閱讀裏的第一個鏈接是英文.再次證明,學好英文是一件多少重要的事。

2010年5月13日 08:45 | # | 引用

Tdou 說:

寫的通俗易懂,謝謝了!

2010年5月20日 14:41 | # | 引用

Harly 說:

此文從框架上說明了字符編碼的套路。我見過太多的不明就裏的人,寫這樣的文章,試圖闡明字符編碼系統,但限於其人先天上大腦邏輯混亂,後天上一知半解,結果就是胡說八道,情形就像:分不清class和instance的區別。

2010年6月28日 13:48 | # | 引用

azziporah 說:

引用Harly的發言:

此文從框架上說明了字符編碼的套路。我見過太多的不明就裏的人,寫這樣的文章,試圖闡明字符編碼系統,但限於其人先天上大腦邏輯混亂,後天上一知半解,結果就是胡說八道,情形就像:分不清class和instance的區別。

非常同意ls的觀點

字符編碼是跨越電子通信和語言文字兩大領域的龐大系統。其一是“編碼”本身,伴隨着電子和通信技術的發展一直在改進,同時也帶來了許多legacy的產物;其二是“字符”,由於人類語言/書寫系統的多樣性和複雜性,將編碼應用到語言文字上來又是一大難題,各種語言文字有各自的出發點和難點。因此,嘗試解決這一混亂局面的UCS/Unicode實在是一項恢弘的工程。無怪乎lz在開頭就說了,這個問題比他想象中複雜。

即使是現在最常見的7-bit ASCII,雖然看上去比較簡單、且有些雜亂無章,實則每個字符的排布都經過了精心的設計,包括性能上的、工程上的、兼容性上的許多考慮因素。

另外,說起字符編碼,就會有許多含糊不清、容易混淆的詞彙,其中很多是基於歷史原因,還有一些以訛傳訛的和巧合的因素,這方面也是需要注意的。

對於大多數人來說,即使您已經系統地學習過數電/計算機課程,我仍然推薦閱讀《編碼的奧祕》。有較好的基礎的人可以參考一下資料

http://scripts.sil.org/Home
http://www.unicode.org/standard/standard.html
http://msdn.microsoft.com/en-us/library/dd318661(v=VS.85).aspx

2010年7月31日 00:54 | # | 引用

盜版小米米 說:

形象生動,易於理解!!!!牛

2010年9月 6日 20:40 | # | 引用

RGLS 說:

非常感謝lz的文章,介紹得很清楚。

2010年9月22日 10:59 | # | 引用

樹樹 說:

寫得很好,一直不太明白字符編碼方面的東西

2010年10月 3日 17:26 | # | 引用

o0o0o 說:

細想起來,可能這是我看到的最好一闡述編碼的文章,精煉易懂,big endian和little endian來源的有趣介紹讓人印象深刻

2010年11月29日 15:11 | # | 引用

atom 說:

文章恆久遠,一篇永流傳。感謝阮兄的勞動!

2011年1月 8日 20:12 | # | 引用

c0ming 說:

big endian和little endian 應該有更好的譯名:大、小端。在網絡編程 和 嵌入式方面都是這樣譯。

2011年1月28日 22:20 | # | 引用

落葉 說:

剛研究UNICODE與ASCII之間的轉換工具,突然想到爲啥UTF-8和UNICODE長得這麼像啊,如果Google了一下,看到這篇文章,真的不錯。

2011年3月 4日 20:55 | # | 引用

yanghe 說:

通俗易懂,支持

2011年3月24日 20:15 | # | 引用

frostmoon 說:

正在學習編碼 兄弟的文章通俗易懂 豁然開朗!

2011年3月30日 11:10 | # | 引用

javie 說:

奇怪 我的“嚴”的UTF8二進制怎麼都是fffe 254e??
試過記事本 ,emeditor,ultraedit都是

2011年4月 4日 10:04 | # | 引用

johnw 說:

拜讀了,最近正在弄Java的亂碼問題,看完了明白了很多原理性的東西!

2011年4月 6日 10:55 | # | 引用

towry 說:

很有幫助啊,謝謝分享。轉到我的QQ空間裏了,慢慢看。

2011年6月12日 20:47 | # | 引用

DerekWu 說:

很好,通俗易懂。謝謝。

2011年6月21日 17:42 | # | 引用

yuhai 說:

文中所提的大端法和小端法用來描述字節序列問題不夠詳細

2011年6月30日 15:32 | # | 引用

zhenyulu 說:

非常好,網上搜了半天資料,還是這篇文章寫得好!

2011年7月20日 13:53 | # | 引用

cumirror 說:

很詳細易懂,贊一個~

2011年7月25日 18:04 | # | 引用

胡營 說:

看了很多資料都沒看懂,這下懂了!贊一個!o(≧v≦)o~~好棒

2011年8月 1日 17:32 | # | 引用

evil39c 說:

終於弄懂UTF-8了,謝謝。

2011年8月24日 15:49 | # | 引用

ray 說:

以漢字”嚴“爲例,Unicode碼是4E25,需要用兩個字節存儲,一個字節是4E,另一個字節是25。

4E25不是code point嗎?應該是經utf8轉換爲E4B8A5這三個字節才涉及字節序問題吧?

2011年9月 2日 17:03 | # | 引用

葫蘆 說:

第四、五節不是很好理解。
很棒的文章。

2011年9月16日 14:31 | # | 引用

森馬 說:

轉你的博客了,對我很有用。多謝。
另外文中連接的unicode的編碼表裏面數據貌似不對。

2011年11月 7日 11:23 | # | 引用

小郭 說:

這篇文章真的很棒 謝謝

2011年11月15日 02:27 | # | 引用

Lidh 說:

引用Ruan YiFeng的發言:

......
UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字節順序是不變的,因此這個文件頭實際上不起作用。有一些編程語言是ISO-8859-1編碼,所以如果用UTF-8針對這些語言編程序,就必須去掉BOM,即保存成“UTF-8—無BOM”的格式纔可以,PHP語言就是這樣。

UTF-8文件的BOM“EF BB BF”,它實際上就是FE FF用UTF-8編碼而得到的

2011年11月24日 11:21 | # | 引用

張彥飛 說:

結合最常用的記事本來介紹Unicode,UTF-8. 太好了!!

2011年12月20日 12:01 | # | 引用

negatlov 說:

引用網友的發言:

windows notepad保存對話框的編碼方式列表中列出“Unicode”這一個名字,實際上是一個通俗的稱呼(來自windows NT的早期版本的習慣,一直沿用),指UTF-16,而不是嚴格的稱呼。希望你行文不要受這個影響。

這個說法很正確。的確換成utf-16準確點。mac osx這上面做得就很好。

2012年1月24日 11:46 | # | 引用

negatlov 說:

引用水中魚的發言:


UTF-16擴充了Unicode,包括了一些稀有字符,想我們國家的滿文,藏文等等,兩者基本上等價

記得utf-16是unicode的具體實現方法。你這種說法本來就有混淆概念。

2012年1月24日 11:48 | # | 引用

Finals 說:

這個可以用來分段讀取UTF-8字符,不錯

2012年2月25日 15:41 | # | 引用

旦旦 說:

藏文最好的那種啊字符編碼?如在QT裏

2012年3月16日 00:43 | # | 引用

pw.cheng 說:

非常感謝樓主的講解,我想知道的你說了好多,這個地址收藏了

2012年5月 2日 17:32 | # | 引用

U雅de凋0 說:

好東西 !謝謝 峯哥 的總結 與 無私奉獻!我不用再 苦苦 搜索了!謝謝

2012年5月 8日 18:22 | # | 引用

葫蘆 說:

引用chen3feng的發言:

 

Windows 從 NT 開始,一開始的內碼是 UCS-2,只支持 Unicode BMP 字符,後來做了擴充,目前的內碼就是 UTF-16,通常不嚴格地稱爲 Unicode。

unicode是字符集,UTF-16是編碼方式。。。 不嚴格地成爲Unicode是咋個意思。。

2012年6月14日 09:45 | # | 引用

qqtoys 說:

寫得很清楚,非常受用。謝謝作者。

2012年6月28日 09:40 | # | 引用

半睡的灰狼 說:

寫的真的挺不錯的! 頂

2012年7月18日 14:16 | # | 引用

mm 說:

Windows 7 簡體中文版用的是GBK編碼,兼容GB2312。

2012年7月26日 14:25 | # | 引用

殘陽 說:

你好,目前被一個問題困擾。。。
是這樣的,有一被壓縮過的流沒有解壓而直接被轉化成字符串了(當然是亂碼了),這是新浪的一個雲應用FetchURL乾的。。。我看了他們的源碼,直接用apache提供的包中的EntityUitls中的toString方法轉化成字符串了,所以我在發送請求的時候在加上請求頭Accept-Encoding:gzip,他們沒有解壓就直接轉化成字符串了。問題來了,我現在的作法是把這個字符串轉化成字節,然後解壓,具體的作法是:用String類中的getBytes(Charset),然後InputStream in1 = new GZIPInputStream(new ByteArrayInputStream(bytes1)),這裏報錯了,這個bytes1的字節已經不是1f 8b開頭的,也就是說不是壓縮過的模式了,求解。。。爲什麼。。難道EntityUitls,把壓縮過的流轉化爲字符串的時候丟了字節碼,還是什麼情況(這裏有個特例,就是ISO-8859-1可以還原,其餘都不行)。。。

2012年7月30日 15:17 | # | 引用

xiaobin 說:

寫的太好了。有個問題想請教下,mysql中是如何用latin1來存中文的。

2012年8月20日 10:40 | # | 引用

Daniel 說:

@殘陽:

關於使用ISO-8859-1,我認爲是因爲:
“ISO-8859-1 字符集的編碼範圍是 0000-00FF,正好和一個字節的編碼範圍相對應。這種特性保證了使用 ISO-8859-1 進行編碼和解碼可以保持編碼數值“不變”。雖然中文字符在經過網絡傳輸時,被錯誤地“拆”成了兩個歐洲字符,但由於輸出時也是用 ISO-8859-1,結果被“拆”開的中文字的兩半又被合併在一起,從而又剛好組成了一個正確的漢字。”

具體可以看搜到的這篇blog:
http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/

2012年8月25日 17:03 | # | 引用

彗星 說:

確實深入淺出,不錯!

2012年9月 5日 16:49 | # | 引用

爛爛 說:

前幾天才從fanfou看到linus的帖 
今天找編碼 就又找到這裏

2012年9月 8日 17:13 | # | 引用

shuiyouren 說:

非常感謝您的分享,我一直在納悶計算機是如何判斷utf-8中的ascii部分,現在完全清晰了,額,竟然一直沒發現ascii保留了最高位爲0……

2012年9月18日 11:40 | # | 引用

icolin 說:

"因此,第一個字節在前,就是”大頭方式“(Big endian),第二個字節在前就是”小頭方式“(Little endian)。
那麼很自然的,就會出現一個問題:計算機怎麼知道某一個文件到底採用哪一種方式編碼?
Unicode規範中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做”零寬度非換行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1。
如果一個文本文件的頭兩個字節是FE FF,就表示該文件採用大頭方式;如果頭兩個字節是FF FE,就表示該文件採用小頭方式。"

文中的big endian 和 little endian,翻譯成“大尾”和“小尾”是不是更恰當?理由如下:
FEFF: FF 比 FE大且FF在後面,顯示就是“大尾”

另外你在延伸閱讀中提到的文章也是翻譯成“大尾”和“小尾” (談談Unicode編碼)

2012年10月23日 16:51 | # | 引用

Poken-Chen 說:

哈哈,謝謝啊。
我有個私人問題可以問一下嗎?
就是如果有了新字符的話?如何添加到Unicode裏呢?怎麼向Unicode或者國家申請嗎?會通過嗎?

2012年10月28日 04:44 | # | 引用

xiekun 說:

我有個疑問“ANSI:文件的編碼就是兩個字節“D1 CF”,這正是“嚴”的GB2312編碼,這也暗示GB2312是採用大頭方式存儲的”

請阮兄指教,不是說用FE,FF來表示大端和小端嗎,爲啥說從上面的編碼可以看出是採用大頭方式的呢

2012年11月 7日 23:14 | # | 引用

FallenMax 說:

感謝阮老師,和所引用資料的作者。好文,清晰易懂。

2012年12月12日 14:28 | # | 引用

情花一衝 說:

我只想說:我很感動,感動樓主的清晰易懂,一天的bug,終於在此貼解決,,,苦逼的程序員,,,想哭 。。。。

2012年12月29日 21:36 | # | 引用

xujinnan 說:

這個問題也是我一直想弄清楚的,今天終於找到了篇好的文章,受益匪淺啊,感謝博主!

2013年1月19日 11:53 | # | 引用

Homer 說:

感謝樓主!要是大學教材都用這種風格來編寫,估計四年的計算機課程,真正在讀書的1年就夠了!!

utf-8的設計的確很巧妙:“2)對於n字節的符號(n>1),第一個字節的前n位都設爲1【後面有幾個字節兄弟們,就設計幾個的顯示位!計算機首先讀到的,就是應該作爲一組來解析的字節兄弟總數】,第n+1位設爲0【表示顯示位結束!】,後面字節的前兩位一律設爲10【表示這些字節都是被剛纔那個leader領導的兄弟們】……”

另請教一下,對於在128—255的ASCII擴展字符,由於利用了最高位,應該就是與utf-8不一樣了吧?

2013年1月20日 17:55 | # | 引用

aisensiy 說:

一直對 utf-8 unicode 很模糊,這篇完整講的太到位了

2013年3月25日 14:45 | # | 引用

ChanneW 說:

可是,以ANSI方式保存的文本文檔也能正常顯示中文啊~

2013年4月 3日 18:52 | # | 引用

David 說:

好早的文章,居然被人推薦過來了,真是很好的科普文章。

關於 UTF-8,它有一個非常好的特性文中應該順帶提一下:雖然不同的字符字節數不同,但是任何一個字符不會是另一個字符的一部分。這使得基於字節的字符串匹配算法可以直接應用在 UTF-8 上。一些新興的語言,如 Go ,字符串處理方面有很大部分是針對 UTF-8 編碼的,個人感覺是空間、執行效率以及編程複雜度比較好的平衡。

2013年4月 8日 15:15 | # | 引用

峯 說:

很容易理解。

2013年4月19日 14:12 | # | 引用

hilojack 說:

引用姬着的發言:

文章裏說:UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。

那USC-2編碼方式如何實現超過兩個字節的符號的存儲?謝謝

ucs2不能實現兩字節以上字符,得使用支持4字節的utf16,我的博客 字符編碼入門有提及

2013年5月27日 02:17 | # | 引用

千鶴 說:

再看一遍效果又不同了,證明自己沒懂。

2013年6月 5日 09:35 | # | 引用

leo569 說:

阮老師的文章,絕對值得一讀。以前寫java的,對底層瞭解相對缺失,現在寫C++遇到這類問題極爲棘手。拜讀了

2013年7月23日 14:22 | # | 引用

FlyingFish 說:

講得通俗易懂,很明白。謝謝作者!

2013年8月 7日 20:16 | # | 引用

聞一多 說:

這樣的基礎的東西很好。

2013年8月 8日 05:16 | # | 引用

小明 說:

好文章,不太長的文章能把事情都說明白,很不簡單。

2013年8月25日 21:38 | # | 引用

peach5460 說:

太有幫助了,我以前從沒真正弄懂過字符編碼...

2013年9月 4日 09:29 | # | 引用

平和的心 說:

有個問題請教一下,Unicode編碼指的是UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。那對於Unicode編碼超過兩個字節的,就沒法保存了?

2013年10月17日 17:35 | # | 引用

遊客 說:

引用平和的心的發言:

有個問題請教一下,Unicode編碼指的是UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。那對於Unicode編碼超過兩個字節的,就沒法保存了?

還有UCS-4唄,當然UCS-2用的比較多而已,可編碼的漢字也僅僅是個子集而已。

2013年10月24日 17:11 | # | 引用

fly 說:

這麼精彩的講解,實在不得不向作者致敬!
內容講解得非常詳細但又通俗易懂,謝謝了~~

2013年11月 5日 11:11 | # | 引用

apollo 說:

你好,我用uncode保存“嚴”, 用hex查看是:e4 b8 a5 00 c4 00。而後三個字節,00 c4 00會因爲的我保存一直在變化,有的時候會是 00 ee 3e,有的時候直接空了~

我用的win7

後來,經過嘗試,又出現了BOM,顯示爲文中提到的ff fe 25 4e

這種情況最讓人蛋疼了,搞不清是爲什麼~
求教樓主

2013年11月15日 15:20 | # | 引用

digdeep126 說:

一直對這一塊沒有完全弄明白,博主真是個打破沙鍋問到底的人。感謝博主的精彩文章。

2013年11月24日 23:11 | # | 引用

loli 說:

博主,這事我迄今看過最通俗易懂的 關於編碼的文章了,太感謝了。之前看過其他很多,都是一頭霧水。。

2013年12月26日 10:22 | # | 引用

wwj 說:

近期發現您的文章,覺得很多都值得收藏,通俗易懂!非常感謝分享知識和想法

2014年1月18日 00:45 | # | 引用

Song 說:

一點小小的問題,有一個地方『根據』的『根』寫爲了『跟』,變成了『跟據』

2014年1月26日 23:36 | # | 引用

le 說:

非常感謝。通俗易懂。

2014年2月10日 09:58 | # | 引用

share 說:

非常通俗易懂

2014年3月20日 10:58 | # | 引用

lan 說:

非常有用,受益匪淺,感謝!

2014年3月24日 11:11 | # | 引用

mll 說:

UTF-8的表示算法:既然第一個字節的"1"的數量就表示了整個當前字符的字節數,爲什麼後續字節還需要"10"作爲前綴,這不是白白浪費了每個字節中的兩個位嗎?

2014年3月31日 12:27 | # | 引用

xxx 說:

引用mll的發言:

UTF-8的表示算法:既然第一個字節的"1"的數量就表示了整個當前字符的字節數,爲什麼後續字節還需要"10"作爲前綴,這不是白白浪費了每個字節中的兩個位嗎?

 

也許是出於容錯的考慮,網絡傳輸或者兼容不夠不嚴謹的編輯器,刪掉漢字等字節字符中的部分字節,會使得整串字符亂碼

2014年4月 4日 09:57 | # | 引用

MR.曹 說:

如果早點看到樓主的文章,今天騰訊的實習生筆試就又多一層把握了!

2014年4月12日 23:02 | # | 引用

ibuick 說:

破特麼幾吧文章 什麼特麼雞巴玩意兒

2014年5月 3日 14:17 | # | 引用

ibuick 說:

就這文章水平 我就呵呵了

2014年5月 3日 14:18 | # | 引用

layoe 說:

感覺博大精深。但依舊受益匪淺。
謝謝lz

2014年5月 7日 11:45 | # | 引用

文太 說:

博主您好,我認爲第5段表格中關於4字節的UTF-8編碼可表示的 Unicode Code Point 的範圍應該是:

0001 0000-001F FFFF

2014年5月12日 01:19 | # | 引用

ppxia 說:

頂一個 講的很透徹。

2014年5月21日 15:48 | # | 引用

Liuruoze 說:

感謝樓主的文章,傳道授業解惑也

2014年6月13日 23:13 | # | 引用

有點甜 說:

引用mll的發言:

UTF-8的表示算法:既然第一個字節的"1"的數量就表示了整個當前字符的字節數,爲什麼後續字節還需要"10"作爲前綴,這不是白白浪費了每個字節中的兩個位嗎?

 

試想,當你處在一個字符串中間時,你不知道當前是一個什麼字符,使用UTF-8能幫你在這種情況下同步到下一個合法字符,否則是不可能的。

2014年6月28日 20:27 | # | 引用

chenjw 說:

謝謝你的精彩分享。但有一個疑問:
"Unicode當然是一個很大的集合,現在的規模可以容納100多萬個符號",我的初步理解是:Unicode既然是2字節的,那麼按理說不會超過256*256個字符纔對。這裏的“100多萬個符號”箇中來由,可否細談下?

2014年7月 3日 19:09 | # | 引用

柚子 說:

引用chenjw的發言:

謝謝你的精彩分享。但有一個疑問:
"Unicode當然是一個很大的集合,現在的規模可以容納100多萬個符號",我的初步理解是:Unicode既然是2字節的,那麼按理說不會超過256*256個字符纔對。這裏的“100多萬個符號”箇中來由,可否細談下?

是的,我也有這個疑問,能否請教樓主,望來信告知![email protected]

2014年7月11日 13:56 | # | 引用

柚子 說:

引用xiaobin的發言:

寫的太好了。有個問題想請教下,mysql中是如何用latin1來存中文的。

嗯,關於mysql這邊,我也有一個問題。
我這邊有一個mysql是默認的latin1的編碼,我的應用是java寫的,我想:“不管mysql的編碼如何,我只要在java應用對編碼做正確的轉換,那麼就應該能夠在寫入和讀出mysql時不出現亂碼纔對。”,請問這個想法是否正確呢?

2014年7月11日 14:16 | # | 引用

dlitchi 說:

引用柚子的發言:

是的,我也有這個疑問,能否請教樓主,望來信告知![email protected]

阮大哥的文章有說過哦,傳送門:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

2014年7月13日 22:15 | # | 引用

Brilliance 說:

引用chenjw的發言:

謝謝你的精彩分享。但有一個疑問:
"Unicode當然是一個很大的集合,現在的規模可以容納100多萬個符號",我的初步理解是:Unicode既然是2字節的,那麼按理說不會超過256*256個字符纔對。這裏的“100多萬個符號”箇中來由,可否細談下?

unicode是字符集, 不是存儲方式, 這個搞明白.
通常所說2個字節的unicode實際上是指utf-16, 這應該是winnt遺留下來的叫法. 實際上unicode包括8位,16位,32位等存儲方式. 其中u8和u32都可以最多用4Byte表示一個字符. 而u16最多用2Byte表示.

2014年8月 8日 22:50 | # | 引用

遊客 說:

【USC-2只能用存儲兩個字節的Unicode,超過這一範圍的符號,它不能表示。】
這個說法是錯誤的。記事本的ucs-2存儲方式類同於utf-16的存儲方式。

2014年8月30日 23:30 | # | 引用

林錦 說:

引用chenjw的發言:

謝謝你的精彩分享。但有一個疑問:
"Unicode當然是一個很大的集合,現在的規模可以容納100多萬個符號",我的初步理解是:Unicode既然是2字節的,那麼按理說不會超過256*256個字符纔對。這裏的“100多萬個符號”箇中來由,可否細談下?

Unicode的編碼空間從U+0000到U+10FFFF,共有1,112,064個碼位(code point)可用來映射字符. Unicode的編碼空間可以劃分爲17個平面(plane),每個平面包含216(65,536)個碼位。
17個平面的碼位可表示爲從U+xx0000到U+xxFFFF,其中xx表示十六進制值從0016到1016,共計17個平面。(Plane 0,1,2,....9,a,b,c,d,e,f,10)
第一個平面稱爲基本多語言平面(Basic Multilingual Plane, BMP),或稱第零平面(Plane 0)。其他平面稱爲輔助平面(Supplementary Planes)。

基本多語言平面內,從U+D800到U+DFFF之間的碼位區段是永久保留不映射到Unicode字符。UTF-16就利用保留下來的0xD800-0xDFFF區段的碼位來對輔助平面的字符的碼位進行編碼。

2014年9月 5日 09:43 | # | 引用

淘魚 說:

好文章就是好文章,都過了這麼多年,我不記得了還是來找這篇文章看看。07年到14年

2014年9月12日 22:33 | # | 引用

goldenshaw 說:

最近在寫編碼方面的一個系列,搜到這裏來了,感覺文中對Unicode及UTF-8這兩個不同層面還是沒說透,可參考我已經寫好的一些http://my.oschina.net/goldenshaw/blog?catalog=536953,講得比較詳細。

另:文中說記事本用的所謂Unicode是UCS-2,其實早就應該是UTF-16了,至少我的系統裏已經是UTF-16,而不是什麼UCS-2,UCS-2只有兩字節,根本無法保存U+FFFF以上碼點的字符。

2014年9月15日 17:05 | # | 引用

九地之方 說:

嗯,雖然有些名詞概念講的不是很清楚,但通俗易懂,還算不錯。
其實博主着重過的語句主要是講字符集和字符編碼關係,例如:“UTF-8是Unicode的實現方式之一”這句話,Unicode就是字符集,它規定一個字符對應的編碼,比如博主說的“嚴”對應的編碼是4E25。而UTF-8是字符編碼,它規定在內存中應該以怎樣的方式來存儲4E25,類似的對應Unicode字符集的字符編碼還有UTF-16等。
最後補充下博主說的大端序(BE)和小端序(LE):每個字節內的比特位不會受大端和小端的影響,例如20H,在大端和小端中的內存順序都是00100000。

2014年10月 5日 18:32 | # | 引用

Ruyi 說:

總結的太好了,節省了好多時間,非常感謝你!

2014年11月11日 15:55 | # | 引用

Victor 說:

引用ChanneW的發言:

可是,以ANSI方式保存的文本文檔也能正常顯示中文啊~

ANSI(注意拼寫不是ASCII)並不是“一種”編碼,而是“多種”編碼的統稱。在簡體中文Windows上,ANSI指GBK編碼;在繁體中文Windows上,ANSI指Big5編碼;在英文Windows上,ANSI指cp437編碼。

2014年11月29日 21:53 | # | 引用

jack 說:

如果UTF8無BOM的情況下,程序應如何識別呢?

2014年12月14日 18:34 | # | 引用

yanhaijing 說:

正需要

2014年12月17日 14:26 | # | 引用

monklof 說:

阮一峯老是說:


UTF-8的編碼規則很簡單,只有二條:
1)對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的unicode碼。因此對於英語字母,UTF-8編碼和ASCII碼是相同的。
2)對於n字節的符號(n>1),第一個字節的前n位都設爲1,第n+1位設爲0,後面字節的前兩位一律設爲10。剩下的沒有提及的二進制位,全部爲這個符號的unicode碼。

 

其中第2點,對於n字節的符號的理解是不是有偏差呢?
再看具體的編碼:


Unicode符號範圍 | UTF-8編碼方式
(十六進制) | (二進制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


其中Unicode 0080~07FF段 和 Unicode 0800~FFFF段都是兩個字節,卻不適用於第2)條 規則的。

 

2015年1月17日 15:16 | # | 引用

xinwendashibaike 說:

就像它的名字都表示的
改成
就像它的名字所表示的

2015年1月20日 15:18 | # | 引用

許湘涵 說:

很不錯 好文章就是解惑有啓發

2015年1月20日 17:28 | # | 引用

揚空 說:

醍醐灌頂

2015年1月26日 16:22 | # | 引用

胡曉曉 說:

還沒看完就趕緊過來留言了,感謝博主,這就是我關心的問題,哈哈,給你個贊。

2015年3月19日 15:37 | # | 引用

Ray 說:

寫的太好了.找了好多篇文章,峯哥的這篇深入淺出,讓我止不住的想盲目膜拜一下.

2015年4月 7日 21:32 | # | 引用

mihooke 說:

嗯!Comments ,wonderfully!

2015年4月24日 12:17 | # | 引用

CiBai 說:

對我的幫助很大。。謝謝你。。。

2015年4月30日 23:50 | # | 引用

朱國偉 說:

你好, 請教一個問題。 已讓我寢食難安好幾天了。如下所示:
mysql會話中執行status:
Server characterset: latin1
Db characterset: latin1
Client characterset: latin1
Conn. characterset: latin1
表結構:
CREATE TABLE `t3` (
`data` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1

mysql> insert into t3 select '嚴';
mysql> select data,hex(data) from t3;
+------+-----------+
| data | hex(data) |
+------+-----------+
| 嚴 | E4B8A5 |
+------+-----------+
奇怪! 不是latin1嗎?怎麼和utf8沒有區別呢?

2015年5月 5日 21:27 | # | 引用

icesnow 說:

我也正在做一個編碼的轉換,請問如何將ansi編碼轉換成utf-8編碼呢?
我曾經試着手動把unicode編碼轉換爲Utf-8編碼就是簡單地遍歷所有的字節數組,然後把0和1刪除。不知道這樣做對嗎?
現在問題是怎麼把ansi編碼轉成utf-8呢?
或者把ansi編碼轉成完全是字節數組的十六進制表示?

2015年5月20日 14:18 | # | 引用

chengbapi 說:

原文:
Unicode符號範圍 | UTF-8編碼方式
(十六進制) | (二進制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

最後一行應該是 0001 0000-001F FFFF

2015年5月22日 17:20 | # | 引用

您的大名 說:

"UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號"

應該是1~6個字節

2015年6月24日 05:25 | # | 引用

OnePiece 說:

請允許我叫你阮兄,哈哈,雖然我才大一計算機專業的學生,不過很喜歡看阮兄你的文章

2015年9月 5日 15:19 | # | 引用

曉薇 說:

unicode編碼轉utf8編碼js實現,代碼感覺寫的不是很好,看到可以簡化一下,
var unicodeToUTF8 = function(unicode) {
if(unicode >= 0x00000000 && unicode return unicode;
}
else if(unicode >= 0x00000080 && unicode var r1 = (((unicode & 0x7C0) >> 6)|0xC0) var r2 = (unicode & 0x03F)|0x80;
return r1 | r2;
}
else if(unicode >= 0x00000800 && unicode var r1 = (((unicode & 0xF000) >> 12)|0xE0) var r2 = (((unicode & 0x0FC0) >> 6)|0x80) var r3 = ((unicode & 0x003F)|0x80);
return r1 | r2 | r3;
}
else if(unicode >= 0x00010000 && unicode var r1 = (((unicode & 0x1C0000) >> 18)|0xE0) var r2 = (((unicode & 0x03F000) >> 12)|0x80) var r3 = (((unicode & 0x000FC0) >> 6)|0x80) var r4 = ((unicode & 0x00003F)|0x80);
return r1 | r2 | r3 | r4;
}
else {
return false;
}
}

2015年10月12日 17:51 | # | 引用

楊傑 說:

寫的真的是通俗易懂,深入淺出,很讓我這樣的小白受益啊。感謝感謝

2015年11月 5日 17:58 | # | 引用

Jason 說:

"EF BB BF" 這個是windows的text editor添加的BOM,用其他的編輯器不一定會出現。

2015年11月 6日 14:49 | # | 引用

hoho 說:

已知"嚴"的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的範圍內(0000 0800-0000 FFFF)這個是啥意思?怎麼對應起來的?

2015年12月27日 14:18 | # | 引用

jingmi 說:

感謝作者的講解,通俗易懂!看過明白很多。

2016年1月 8日 15:59 | # | 引用

wier 說:

引用柚子的發言:

 

是的,我也有這個疑問,能否請教樓主,望來信告知![email protected]

統一碼的編碼方式與ISO 10646的通用字符集概念相對應。目前實際應用的統一碼版本對應於UCS-2,使用16位的編碼空間。也就是每個字符佔用2個字節。這樣理論上一共最多可以表示216(即65536)個字符。基本滿足各種語言的使用。實際上當前版本的統一碼並未完全使用這16位編碼,而是保留了大量空間以作爲特殊使用或將來擴展。

上述16位統一碼字符構成基本多文種平面。最新(但未實際廣泛使用)的統一碼版本定義了16個輔助平面,兩者合起來至少需要佔據21位的編碼空間,比3字節略少。但事實上輔助平面字符仍然佔用4字節編碼空間,與UCS-4保持一致。未來版本會擴充到ISO 10646-1實現級別3,即涵蓋UCS-4的所有字符。UCS-4是一個更大的尚未填充完全的31位字符集,加上恆爲0的首位,共需佔據32位,即4字節。理論上最多能表示231個字符,完全可以涵蓋一切語言所用的符號。

基本多文種平面的字符的編碼爲U+hhhh,其中每個h代表一個十六進制數字,與UCS-2編碼完全相同。而其對應的4字節UCS-4編碼後兩個字節一致,前兩個字節則所有位均爲0。

關於統一碼和ISO 10646及UCS的詳細關係,見通用字符集。
維基百科上,有輔助平面字符

2016年1月14日 14:38 | # | 引用

奧丁1號 說:

通俗易懂

2016年2月17日 10:31 | # | 引用

劉晉霞 說:

受益匪淺

2016年2月22日 11:49 | # | 引用

心聲 說:

您好,非常感謝,真的是通俗易懂,果然是大神

發現文章中有個地方寫差了:
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

應該是:
0001 0000-001F FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

再次感謝。

2016年3月12日 13:21 | # | 引用

sigh 說:

文中

```
Unicode符號範圍(十六進制) | UTF-8編碼方式(二進制)
--------------------|---------------------------------------------
0000 0000 - 0000 007F | 0xxxxxxx
0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
```

的最後一行應該爲

```
Unicode符號範圍(十六進制) | UTF-8編碼方式(二進制)
--------------------|---------------------------------------------
0000 0000 - 0000 007F | 0xxxxxxx
0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 001F FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
```

2016年3月20日 16:42 | # | 引用

kiscms 說:

1、Unicode符號範圍的最大值爲什麼是 0X10FFFF ?
2、0X10FFFF 的二進制是 00010000 11111111 11111111 ,很明顯它是三個字節的,那麼對應轉成utf8的規則就是1110xxxx 10xxxxxx 10xxxxxx ,可是爲什麼文中要對應成 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx,我想知道,這張表是按什麼規律生成的?

2016年4月14日 15:49 | # | 引用

Zack 說:

清晰,易懂。

2016年4月24日 21:39 | # | 引用

Lemon 說:

講得簡潔易懂,棒

2016年7月10日 12:50 | # | 引用

張鼕鼕 說:

感謝博主!最近在解決java用jni調用C++中的一些編碼方式問題,這篇文章深入淺出、講解得清晰又易懂!幫了我大忙了,又學了些知識

2016年8月 3日 19:46 | # | 引用

Neo 說:

真難想象博主在07年就整理出這詳盡的計算機文檔, 在零散的互聯網知識裏面你的 Blog最能激發我認真閱讀的興趣! :)

2016年8月27日 23:40 | # | 引用

博主崇拜者 說:

經典,通俗易懂,條例清晰。

2016年10月11日 17:41 | # | 引用

博主崇拜者 說:

好的文章,十年後有人看了都會忍不住回覆。

2016年10月11日 17:42 | # | 引用

alias 說:

文章通俗易懂,贊

2017年1月18日 17:59 | # | 引用

-_-宋宋-_- 說:

知識果然不易過時,收藏~~

2017年2月 8日 10:28 | # | 引用

Weiting 說:

非常有用的文章!! 謝謝你~~

2017年3月22日 17:47 | # | 引用

伯格 說:

老師好,下面這句話我應該怎麼理解呢?
《這裏就有兩個嚴重的問題,第一個問題是,如何才能區別Unicode和ASCII?計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?》

既然不同的編碼會用不同的編碼解讀,那麼怎麼會出現不能區分Unicode和ASCII的問題?我用ASCII解讀不就可以了嗎?

所以我太懂這個問題的怎麼出現的?

2017年5月16日 17:56 | # | 引用

Asun 說:

看過老師很多文章,每一篇都是好文,感謝老師分享

2017年5月22日 15:46 | # | 引用

chappie 說:

引用伯格的發言:

老師好,下面這句話我應該怎麼理解呢?
《這裏就有兩個嚴重的問題,第一個問題是,如何才能區別Unicode和ASCII?計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?》

既然不同的編碼會用不同的編碼解讀,那麼怎麼會出現不能區分Unicode和ASCII的問題?我用ASCII解讀不就可以了嗎?

所以我太懂這個問題的怎麼出現的?

用unicode編碼的文件,所有的字符都用兩個字節編碼,ASCII也是,所以不會存在這個區分的問題

2017年8月17日 16:34 | # | 引用

Jixuan Cheng 說:

你太有教育人的天賦了。我現在編程有不明白的地方,如果是基礎的不理解,就找你的網來讀!都能明白!真棒!謝謝

2017年8月20日 00:21 | # | 引用

mccasey 說:

參考這個utf8編碼對照表更清晰,來自https://en.wikipedia.org/wiki/UTF-8

Number Bit for first last
of bytes code points code points code points Byte 1 Byte 2 Byte 3 Byte 4
1 7 U+0000 U+007F 0xxxxxxx
2 11 U+0080 U+07FF 110xxxxx 10xxxxxx
3 16 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 21 U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

2017年8月23日 14:22 | # | 引用

hk4fun 說:

http://www.cnblogs.com/leesf456/p/5317574.html

2017年8月25日 01:40 | # | 引用

劉江龍 說:

讀了你很多文章了,這篇也有收穫。 這個例子做了一個unicode到UTF-8字符集的掩碼運算,mark到這裏備忘。
漢字 嚴的 unicode碼點 4E25
0100 1110 0010 0101
1110 xxxx 10xx xxxx 10xx xxxx 
1110 0100 1011 1000 1010 0101

2017年8月31日 20:38 | # | 引用

汗青 說:

謝謝分享,真的很感謝,願意分享的自己知識的人都是好樣的!

2017年9月 7日 10:44 | # | 引用

wsj 說:

1,
ascii碼由美國人發明,長度爲一個字節,8個二進制,最長表示256個字符,
標準的ascii爲128個,前面一個bit爲0,用後面7位表示128個ascii碼。
2,
爲了兼容世界所有字符,重新編了一套Unicode,每個符號給予獨一無二的編碼,
現在可容納100多萬字符,這就意味着Unicode的編碼變得特別多,也就不可能用1個
字節表示ASCII嗎,因爲解碼的時候所有比特連在一起,會將你本來用來表示ascii碼的
一個字節混淆(因爲unicode編碼太多,此ascii的編碼可能與Unicode某個編碼中的某
一段重複);所以必須加長用來表示ascii的編碼,以進行區別,但這就帶來一個浪費內
存的問題。
3,
所以出現了utf8,它用前N+1個比特表示它的長度N(除了單字節是用一個0比特表示,這時
的utf8編碼與ascii編碼一致,都是一個字節),後面的編碼直接從Unicode編碼裏面拿。所以它
不是全新的編碼,是Unicode的一種實現方式,相比而言節省了內存(尤其是在以英文居多的時候)。

2017年10月 4日 11:46 | # | 引用

wyq 說:

在這篇文章剛好十年的時候看到了。。。

2017年10月28日 18:39 | # | 引用

xxx 說:

10 year anni

2017年10月28日 22:24 | # | 引用

HeroTimor 說:

十年後來看一下,當時阮哥的博客略顯青澀哦。

2017年10月30日 09:40 | # | 引用

noans 說:

引用心聲的發言:

您好,非常感謝,真的是通俗易懂,果然是大神

發現文章中有個地方寫差了:
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

應該是:
0001 0000-001F FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

再次感謝。

UTF-8的最大值之所以是 10FFFF 而不是 1FFFFF 是因爲Unicode的範圍是 U+0-10FFFF
下面的鏈接是Unicode的範圍設置原因討論的帖子
http://unicode.org/mail-arch/unicode-ml/Archives-Old/UML021/0881.html

2017年11月24日 10:48 | # | 引用

小張 說:

每個字符在Unicode裏面的位置是一定的,爲什麼不能直接使用這個位置進行二進制存儲呢,也能做到動態大小存儲吧?

2018年4月10日 20:43 | # | 引用

會飛小超人 說:

最近在找字符編碼的文章,看了好幾篇,果然還是阮老師的最清晰易懂!

2018年5月28日 16:54 | # | 引用

我要發表看法

您的留言 (HTML標籤部分可用)

 

您的大名:

 «-必填

電子郵件:

 «-必填,不公開

個人網址:

 «-我信任你,不會填寫廣告鏈接

記住個人信息?

 «- 點擊按鈕

微博 | 推特 | GitHub

2018 © 我的郵件 |

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章