佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > Unicode編碼大揭秘

Unicode編碼大揭秘

熱門標簽:智能手機 銀行業(yè)務 網站文章發(fā)布 鐵路電話系統 檢查注冊表項 服務器配置 美圖手機 呼叫中心市場需求

如果你是一個生活在2003年的程序員,卻不了解字符、字符集、編碼和Unicode這些基礎知識。那你可要小心了,要是被我抓到你,我會讓你在潛水艇里剝六個月洋蔥來懲罰你。

這個邪惡的恐嚇是Joel Spolsky在十年前首次發(fā)出的。不幸的是,很多人認為他只是在開玩笑,因此,現在仍有許多人不能完全理解Unicode,以及Unicode、UTF-8、UTF-16之間的區(qū)別。這就是我寫這篇文章的原因。

言歸正傳,設想在一個晴朗的下午,你收到一封電子郵件,它來自一個你高中之后就失去聯系的朋友,并帶有一個txt格式(也稱為純文本格式)的附件。這個附件包含下面這樣一串二進制bits:

復制代碼 代碼如下:

0100100001000101010011000100110001001111

Email的正文是空的,這使它更加神秘。在你啟動常用的文本編輯器打開這個附件之前,你有沒有想過,文本編輯器是怎么將二進制形式翻譯成字符的?這其中有兩個關鍵問題:

1.字節(jié)是怎樣分組的?(例如1個字節(jié)的字符和2個字節(jié)的字符)

2.一個或多個字節(jié)是怎么映射到字符上的?

這些問題的答案就在這篇文檔(Character Encoding)中,大致說來,編碼定義了兩件事:

1.字節(jié)是怎么分組的,如8 bits或16 bits一組,這也被稱作編碼單元。

2.編碼單元和字符之間的映射關系。例如,在ASCII碼中,十進制65映射到字母A上

字符編碼和字符集之間有微小的區(qū)別。不過通常它和你無關,除非你在設計一個底層的庫。

ASCII碼是上個世紀最流行的編碼體系之一,至少在西方是這樣。下圖顯示了ASCII碼中編碼單元是怎么映射到字符上的。

有一個即使在經驗豐富的程序員中也非常常見的誤解就是,純文本使用ASCII碼并且每個字符都是8 bits。

事實是,沒有這樣的「純文本」。如果在內存或者硬盤中有一個你不知道編碼的字符串,那你就無法翻譯或者顯示它。這絕對沒有第二條路可選。

那么當你剛剛收到的附件沒有指定編碼格式的時候,計算機會如何翻譯它呢?這是否意味著你就永遠也讀不到失去聯系的老朋友想跟你說的話了呢?在我們找到答案之前,我們首先回到那個年代————那個用錢能買到的最大硬盤是29MB的時代。

歷史回顧

很久以前,計算機制造商有自己的表示字符的方式。他們并不需要擔心如何和其它計算機交流,并提出了各自的方式來將字形渲染到屏幕上。隨著計算機越來越流行,廠商之間的競爭更加激烈,在不同的計算機體系間轉換數據變得十分蛋疼,人們厭煩了這種自定義造成的混亂。

最終,計算機制造商一起制定了一個標準的方法來描述字符。他們定義使用一個字節(jié)的低7位來表示字符,并且制作了如上圖所示的對照表來映射七個比特的值到一個字符上。例如,字母A是65,c是99,~是126等等, ASCII碼就這樣誕生了。原始的ASCII標準定義了從0到127 的字符,這樣正好能用七個比特表示。不過好景不長……


為什么選擇了7個比特而不是8個來表示一個字符呢?我并不關心。但是一個字節(jié)是8個比特,這意味著1個比特并沒有被使用,也就是從128到255的編碼并沒有被制定ASCII標準的人所規(guī)定,這些美國人對世界的其它地方一無所知甚至完全不關心。

其它國家的人趁這個機會開始使用128到255范圍內的編碼來表達自己語言中的字符。例如,144在阿拉伯人的ASCII碼中是گ,而在俄羅斯的ASCII碼中是ђ。即使在美國,對于未使用區(qū)域也有各種各樣的利用。IBM PC就出現了“OEM 字體”或”擴展ASCII碼”,為用戶提供漂亮的圖形文字來繪制文本框并支持一些歐洲字符,例如英鎊(£)符號。

再強調一遍,ASCII碼的問題在于盡管所有人都在0-127號字符的使用上達成了一致,但對于128-255號字符卻有很多很多不同的解釋。你必須告訴計算機使用哪種風格的ASCII碼才能正確顯示128-255號的字符。

這對于北美人和不列顛群島的人來說不算什么問題,因為無論使用哪種風格的ASCII碼,拉丁字母的顯示都是一樣的。英國人還需要面對的問題是原始的ASCII碼中不包含英鎊符號,但是這個已經無關緊要了。

與此同時,在亞洲有更讓人頭疼的問題。亞洲語言有更多的字符和字形需要被存儲,一個字節(jié)已經不夠用了。所以他們開始使用兩個字節(jié)來存儲字符,這被稱作DBCS(雙字節(jié)編碼方案)。在DBCS中,字符串操作變得很蛋疼,你應該怎么做str++或str–?

這些問題成為了系統開發(fā)者的噩夢。例如,MS DOS必須支持所有風格的ASCII碼,因為他們想把軟件賣到其他國家去。他們提出了「內碼表」這一概念。例如,你需要告訴DOS(通過使用”chcp”命令)你想使用保加利亞語的內碼表,它才能顯示保加利亞字母。內碼表的更換會應用到整個系統。這對使用多種語言工作的人來說是一個問題,因為他們必須頻繁的在幾個內碼表之間來回切換。

盡管內碼表是一個好主意,但是它不是一個簡潔的解決方案,它只是一個hack技術或者說是簡單的修正來讓編碼系統可以工作。

進入Unicode的世界

最終,美國人意識到他們應該提出一種標準方案來展示世界上所有語言中的所有字符,以便緩解程序員的痛苦和避免字符編碼引發(fā)的第三次世界大戰(zhàn)。出于這個目的,Unicode誕生了。

Unicode背后的想法非常簡單,然而卻被普遍的誤解了。Unicode就像一個電話本,標記著字符和數字之間的映射關系。Joel稱之為「神奇數字」,因為它們可能是隨機指定的,而且不會給出任何解釋。官方術語是碼位(Code Point),總是用U+開頭。理論上每種語言中的每種字符都被Unicode協會指定了一個神奇數字。例如希伯來文中的第一個字母א,是U+2135,字母A是U+0061。

Unicode并不涉及字符是怎么在字節(jié)中表示的,它僅僅指定了字符對應的數字,僅此而已。

關于Unicode的其它誤解包括:Unicode支持的字符上限是65536個,Unicode字符必須占兩個字節(jié)。告訴你這些的人應該去換換腦子了。

記住,Unicode只是一個用來映射字符和數字的標準。它對支持字符的數量沒有限制,也不要求字符必須占兩個、三個或者其它任意數量的字節(jié)。

Unicode字符是怎樣被編碼成內存中的字節(jié)這是另外的話題,它是被UTF(Unicode Transformation Formats)定義的。

Unicode編碼

兩個最流行的Unicode編碼方案是UTF-8和UTF-16。讓我們看看它們的細節(jié)。

UTF-8

UTF-8是一個非常驚艷的概念,它漂亮的實現了對ASCII碼的向后兼容,以保證Unicode可以被大眾接受。發(fā)明它的人至少應該得個諾貝爾和平獎。

在UTF-8中,0-127號的字符用1個字節(jié)來表示,使用和US-ASCII相同的編碼。這意味著1980年代寫的文檔用UTF-8打開一點問題都沒有。只有128號及以上的字符才用2個,3個或者4個字節(jié)來表示。因此,UTF-8被稱作可變長度編碼。

回到文章開始的問題,來自你老朋友的附件的字節(jié)流如下:

復制代碼 代碼如下:

0100100001000101010011000100110001001111

這個字節(jié)流在ASCII和UTF-8中表示相同的字符:HELLO

UTF-16

另一個流行的可變長度編碼方案是UTF-16,它使用2個或者4個字節(jié)來存儲字符。然而,人們逐漸意識到UTF-16可能會浪費存儲空間,但那是另一個話題了。

低字節(jié)序(Little Endian)和高字節(jié)序(Big Endian)

Endian讀作End-ian或者Indian。這個術語的起源可以追溯到格列佛游記。(小說中,小人國為水煮蛋應該從大的一端(Big-End)剝開還是小的一端(Little-End)剝開而爭論,爭論的雙方分別被稱為“大端派”和“小端派”。)

低字節(jié)序和高字節(jié)序只是一個關于在內存中存儲和讀取一段字節(jié)(被稱作words)的約定。這意味著當你讓計算機用UTF-16把字母A(占兩個字節(jié))存在內存中時,使用哪種字節(jié)序方案決定了你把第一個字節(jié)放在第二個字節(jié)的前面還是后面。這么說有點不太容易懂,讓我們來看一個例子:當你使用UTF-16存下來自你朋友的附件時,在不同的系統中它的后半部分可能是這樣的:

00 68 00 65 00 6C 00 6C 00 6F(高字節(jié)序,高位字節(jié)被存在前面)

68 00 65 00 6C 00 6C 00 6F 00(低字節(jié)序,低位字節(jié)被存在前面)

字節(jié)序方案只是一個微處理器架構設計者的偏好問題,例如,Intel使用低字節(jié)序,Motorola使用高字節(jié)序。

字節(jié)順序標記(BOM)

如果你經常要在高低字節(jié)序的系統間轉換文檔,并且希望區(qū)分字節(jié)序,還有一種奇怪的約定,被稱作BOM。BOM是一個設計得很巧妙的字符,用來放在文檔的開頭告訴閱讀器該文檔的字節(jié)序。在UTF-16中,它是通過在第一個字節(jié)放置FE FF來實現的。在不同字節(jié)序的文檔中,它會被顯示成FF FE或者FE FF,清楚的把這篇文檔的字節(jié)序告訴了解釋器。

BOM盡管很有用,但并不是很簡潔,因為還有一個類似的概念,稱作「魔術字」(Magic Byte),很多年來一直被用來表明文件的格式。BOM和魔術字間的關系一直沒有被清楚的定義過,因此有的解釋器會搞混它們。

恭喜你讀到這里,你一定是一個很有耐心的讀者。

還記得文章開頭的問題嗎,既然沒有「純文本」文件這回事,那你的文本編輯器和瀏覽器為什么每次都能正確的顯示內容呢?答案是,那些軟件欺騙了你,這也是為什么那么多人對編碼一無所知。當軟件不能確定編碼的時候,它會猜測。大部分時候,它會猜測是否是涵蓋了ASCII碼的UTF-8,還是ISO-8859-1,也有可能猜其他能想到的任意字符集。因為英文中使用的拉丁字母表在幾乎所有的字符集中都能顯示,包括UTF-8,所以即使編碼猜錯了,英文字母看起來也是正確的。

但是,如果你在瀏覽網頁時看到�符號,這意味著這個網頁的編碼不是你的瀏覽器猜測的那個。這時你可以點開瀏覽器的查看——>字符編碼菜單來嘗試不同的編碼。

總結

如果你沒時間讀整篇文章或者你僅僅是略讀了一下前面的內容。那請你確保你能理解下面的幾條:

這個世界上從來沒有純文本這回事,如果你想讀出一個字符串,你必須知道它的編碼。

Unicode是一個簡單的標準,用來把字符映射到數字上。Unicode協會的人會幫你處理所有幕后的問題,包括為新字符指定編碼。

Unicode并不告訴你字符是怎么編碼成字節(jié)的。這是被編碼方案決定的,通過UTF來指定。

還有最重要的:

永遠記得通過Content-Type或者meta charset標簽來顯式指定你的文檔的編碼。這樣瀏覽器就不需要猜測你使用的編碼了,他們會準確的使用你指定的編碼來渲染文檔。

您可能感興趣的文章:
  • utf8和unicode編碼究竟是什么關系?有何區(qū)別?
  • UTF-8 Unicode Ansi 漢字GB2321幾種編碼轉換程序
  • Encode/DecodeANSIlt;-gt;UTF8兩個編碼工具 下載
  • 淺析c++ 宏 #val 在unicode下的使用
  • 淺析內存對齊與ANSI C中struct型數據的內存布局
  • java實現十六進制字符unicode與中英文轉換示例
  • Mysql中的排序規(guī)則utf8_unicode_ci、utf8_general_ci的區(qū)別總結
  • VC中實現GB2312、BIG5、Unicode編碼轉換的方法
  • C語言中字符和字符串處理(ANSI字符和Unicode字符)

標簽:新疆 河南 滄州 紅河 樂山 上海 長治 沈陽

巨人網絡通訊聲明:本文標題《Unicode編碼大揭秘》,本文關鍵詞  ;如發(fā)現本文內容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    米脂县| 古蔺县| 深水埗区| 宣城市| 苍溪县| 阿拉善右旗| 买车| 个旧市| 柘荣县| 大新县| 宜川县| 河池市| 龙海市| 海南省| 东乡| 休宁县| 师宗县| 贞丰县| 阿克苏市| 河南省| 梁平县| 米易县| 始兴县| 鄱阳县| 上虞市| 长春市| 镇江市| 平陆县| 读书| 白水县| 寻乌县| 翼城县| 噶尔县| 大同市| 渝中区| 丽水市| 西充县| 凤庆县| 日喀则市| 桂阳县| 新竹市|