在計算機中為何不直接使用UTF8編碼進行存儲,而要使用Unicode再轉換成UTF8?
在看到計算機編碼問題時,文章舉例:
"計算機內存中,統一使用Unicode編碼,需要保存或者傳輸時,轉換成UTF8編碼。"我有個疑問為何不直接在計算機里使用UTF8編碼呢?而要進行一次轉換?(我知道UTF8比Unicode利用空間合理,那為何不直接使用UTF8)
謝謝回答
(人在地鐵圖有點av畫質不好意思)
因為一些歷史原因,Windows記事本的「Unicode」這個名號其實相當有誤導性。這個編碼實際上是UTF-16 LE。
Unicode的字元碼,很少在計算機中直接用在存儲和表達文本上。原因無他:太浪費空間了。Unicode字元碼是32位,4個位元組。平常使用的字元里,99%以上的字元都不會突破2個位元組。
為了節省空間,人們就對Unicode的字元碼再做二次編碼,這就誕生了UTF-8,UTF-16,UTF-32等編碼標準。除了UTF-32,UTF-8和UTF-16都是不定長的編碼,他們的意思是按照編碼規則,將一個Unicode字元的字元碼,編碼成N個8位或者N個16位。至於N是幾,要看具體的字元來定。UTF-32例外的原因是,他已經足夠直接保存Unicode的字元碼了……
LE是「小端」的縮寫。因為只要是多位元組的數據,就有端序問題,也就是高位位元組在先還是低位位元組在先的問題。windows平台默認小端,即低位位元組在先。
Windows身上的「歷史原因」,在於Unicode標準初生的時候,字元碼其實是16位,那個時候的UTF-16就能直接保存Unicode字元碼。於是Windows就直接將自己使用的UTF-16 LE編碼命名為「Unicode",這在當時是名符其實的。但是後來西方人尷尬地發現如果把中國人故紙堆里的罕用字,各種小語種的文字都收進去的話,16位65536個碼位仍然是不夠用的。Unicode升級成了32位,出現了字元碼突破16位的字元,UTF-16從定長碼變成了不定長碼,對於Windows來說,這就很坑爹了。儘管字元碼在16位以內的字元,UTF-16編碼仍然保持不變,但還是很坑爹。
在保存和傳輸文本的時候,用UTF-8很多,是因為對於大量以拉丁字母等ANSI字元為主的文獻,UTF-8非常節省空間。但計算機處理文本的時候,內存中一般都不用UTF-8。因為UTF-8是變長編碼,不從頭掃描一遍,你不知道第幾個字元在哪個位置上,這在處理的時候非常浪費時間。現在很多語言/程序的處理辦法,是使用源於原始UTF-16的一個定長編碼,只處理字元碼在16位以內的字元,不支持超過16位的罕見字。這種16位定長的編碼方式被稱為UCS-2。那些零星的幾個突破16位的字元,除非你專門研究古文或者奇怪的小語種,一般來說是遇不到的。遇到了也是黑人問號臉。比如,還有。
嗯,我提交後就發現知乎後台就不幸被我命中,無力處理這兩個擴展漢字,於是只好編輯一下,用圖來表示他們。他們是以及
能處理所有Unicode字元的語言/程序也是有不少的。不怕浪費空間的,就用UTF-32,不怕浪費處理時間的,就用完整的UTF-16,或者索性用UTF-8。
暑假時候也在糾結這個問題,後來查了查資料寫了篇博客
希望對UP有所幫助
計算機的字元編碼是所有人每天都用得到,見得到,但是似乎在基礎計算機課程教材很少被關注的內容(例如佛羅贊的《計算機科學導論》對各種編碼也只提了一頁不到的內容)。互聯網上容易查到的往往是某個特定編碼如何實現的技術細節,而我們平時會接觸到的GB,ASCII,Unicode,UTF-8,UTF-16等術語所指代的標準分別代指了什麼意思?為什麼字元編碼標準會發展成今天這個樣子?都是很少被提及的。絕大多數人並沒有必要知道這些標準的技術細節,因為我們不會去用,但是另一方面,完全不知道這些為什麼如今我們會採用這些字元編碼似乎也不太好。因此作為簡要介紹,本文不會談技術實現細節,只簡要的談一談為什麼當前通用的字元編碼會是我們看到的這個樣子。本文為原創,我尚屬初學,若哪裡有錯誤請多指教。
在文章之前要談一談字元代碼和字元編碼的差異。計算機可以表示的字元組成了一個集合,這個集合我們稱為字符集,和數學中定義集合是無序的不同,字符集中的每一個字元都擁有一個唯一且不和其它字元重複的序號,作為在這個字符集里的位置的標識。例如今天通用的Unicode標準,用的字符集稱之為UCS-4(4-bytes Universal Character Set,4比特通用字符集),裡面的每一個字元的標識都是4比特32位的二進位數,這個數我們稱之為字元代碼。字元編碼則是指字元在傳輸過程當中用於表示字元的二進位序列。按照直覺這兩個東西理應一致,例如ASCII的代碼和編碼就是一樣的,但是其實也可以不一樣,這就引出一個問題,為什麼要定義和字元代碼不一樣的字元編碼?這就是UTF和Unicode主要要討論的問題,但在在「標準信息交換」和「16位與32位的紛爭」中,出於論述方便,我會混淆字符集概念,字元代碼概念和字元編碼概念三者的關係。因為很多時候這些概念雖然不能說等價,但也沒有區分的必要,只有在討論Unicode的傳輸標準和字符集序號的差異時(其實是信息壓縮時),這兩者才有被分開討論的必要。
標準信息交換計算機字元編碼,最初使用的是ASCII碼(America Standard Code for Information Interchange,美國標準信息交換碼),由於ASCII的字元代碼和編碼一致,我就不在術語上區分了。每個字元用一個位元組表示。8位中用了7位保存128種字元對應的二進位代碼,最後一位作奇偶校驗。之所以要有這個標準,是因為計算機只能處理0和1,在傳輸和保存字元時,傳輸和保存的實際上是比特流而非字元本身。計算機需要一套二進位代碼和字元之間的一一映射關係,使得計算機能夠在讀取到特定的比特流時,能夠把這個比特流識別成一個個的字元,在存儲字元時,也能夠按照這個規則把每個字元保存為可被識別的二進位代碼。如果這個映射是不唯一的,那麼在不同設計的計算機上進行信息交互時,就會出問題(計算機A能讀到計算機B傳過來的正確的比特流,但是A無法還原比特流包含的信息)。因此信息交互時要有一個統一的標準,保證各方的信息在相互交換之後可以正常通行,美國國家標準協會(American National Standard Institute,ANSI)據此需求設定ASCII碼,規定了所有字元交互時的二進位代碼和字元之間的對應關係,因此ASCII叫做標準信息交換碼。
由於ASCII碼的設計只考慮了英語,故只用了8位作為單元最多表示256種字元,因此在全球推廣時遇到了很大的尷尬,ASCII的先天缺陷使其無法克服字元不夠用這種障礙。在計算機推廣到全世界的初期,全世界都遇到了ASCII編碼無法映射本民族語言文字的問題。因此當時全世界各個地區都在根據自己的需求拓展字元編碼標準。由於傳輸和保存字元本質上處理的全部都是比特流,美國國家標準協會制定ASCII,提出了英語和二進位碼的對應關係,建立信息的交互標準,那其它國家自然也可以自己設定規則建立本國語言和二進位代碼之間的對應關係,從而滿足本國語言在計算機上的交互需求。我們國家自1980年就有自己的GB2312標準(字元編碼和代碼依舊一致),簡單來說就是在兼容ASCII的前提下,建立了16位的字符集,定義了二進位代碼和中文的映射關係,用兩個位元組表示一個漢字,字元編碼直接就用字元代碼的值。由於兩個位元組就是16位,這樣的字符集理論上最多可以包含65536個字元,基本可以滿足漢語的正常交互需求。這一套標準不斷進行更新,直至今天Windows 10簡體中文版(大陸發行)的記事本的默認保存編碼,ANSI,使用的字元編碼就是我國的自定的國家標準,當然為什麼我國的字元編碼國家標準在Windows 10顯示的名字是ANSI,也就是美國標準,這是一個很迷的事情,似乎多數人認為這是微軟瞎取名字習慣的體現。就當前,2016年,Unicode已經廣泛使用的今天,ANSI旨在解決這樣一種情況:Windows中或許要碰到那些幾十年前寫的老文檔,那麼系統保留以往的以往的字元編碼方式從而能夠正確打開老文檔是必要的。ANSI這種默認編碼,根據各個國家和地區歷史上自己定義的字符集,地區之間不統一,大陸是國家標準總局發布的國家標準(GB),台灣的繁體中文版則是Big 5。
而在幾十年前情況,複雜得多,因為各個國家各行其是,哪怕是台灣和大陸這樣使用同一語言的地區,由於政府不同,也建立了兩套不一樣的字元編碼標準。那麼台灣的數據文檔在大陸打開,由於大陸在售的計算機大概不會預裝台灣的編碼標準,大陸就無法正確讀取台灣文檔中的字元。類似的問題在全球廣泛出現,使得信息在跨國跨語言時的交互非常麻煩。在出版業則更加麻煩,出版業終歸會有在一個文檔里需要處理很多種語言的需求,各個語言的編碼標準完全不同(甚至不同語言中的字元編碼是重疊的)這就讓同時要處理多鍾語言的出版業很難辦。因此從上世紀八十年代初開始,多語言編碼方案就開始進入行業(可怕,8051是81年推出的,那還是8位機的年代),例如IBM和施樂公司(畢竟是印表機鼻祖)都有多語言編碼的產品。隨之而來的趨勢就很顯然了,各個國家都要用計算機,各個國家獨立搞出的字元編碼標準會在通用性上有很大的麻煩,那麼,應該建立一套全球統一的編碼系統,包含所有國家的文字字元,全球所有的計算機都應該只用這一套標準,從而實現標準信息交換,這樣全球傳輸都不會有任何字元無法識別的問題。
16位與32位的紛爭
一個很顯而易見的問題是,要容納全球所有文字字元,字符集要拓展,按照計算機的習慣,存儲單元都是2的冪指數大小的。那麼應該是拓展到一個字元兩個位元組,還是四個位元組?4個位元組和2個位元組反映在文檔上是差整整一倍的大小,當年不像現在,存儲對大部分人來說便宜的跟不要錢一樣,網速又很快,網路中的主導流量是多媒體也不是文字。當年不論計算機本身還是存儲空間都很昂貴,因此16位和32位所造成的文檔差一倍大小,涉及到了極大的經濟利益,標準如果選的有問題,就會在推廣上遭到很大阻力,因此能用16肯定用16。
通用多語言字元標準在研發時,存在兩套完全平行的開發研究項目。第一套是美國,主要是施樂公司主導的,名字叫做Unicode(施樂的帕洛阿爾托研究中心真的是Bug,Unicode一開始也是他們倒騰出來的),建立的字符集稱之為UCS-2(2比特通用字符集,因為字元代碼只有16位,所以最多表示65536個字元),Wikipedia上Unicode詞條的參考資料中可以看到Unicode的第一個版本,Unicode 88的綜述,我只挑最重要的談。Unicode最初的設計文檔除了闡釋通用字元編碼工程的重要性之外,談的最關鍵的問題就是16位夠不夠用,因為哪怕僅僅是在八十年代,對於旨在提供全球統一的字元編碼的Unicode,最多65536個字元似乎也是少了一點。Unicode 88當然解釋了為什麼65536夠用,理由在於兩點。其一,Unicode 88的制定者說,我們制定的標準是面向未來的,只要加入現在還在用的字元就好,那些正在消亡語言的字元我們覺得就不要放在標準里占空間了,而判定字元是「Modern Use」還是「Obsolete」的依據是,是否還出現在當代的出版物中。他們(自稱)統計了1988年全世界所有發行的報紙和雜誌,認為一整年都沒有出現在這些出版物的字元是正在消亡的,沒有必要加入標準。其二,對於世界上字元最多的漢字,針對中日韓三國的漢字很多都是類似的特點,因此可以把中日韓類似的漢字統一表示(也就是所謂的「中日韓統一字元」,我查了一下,這是一段中日韓互相撕逼和對美國撕逼的血雨腥風,但是和主題的關聯度不是特別大就不寫了),避免了三國語言的重複輸入。這樣兩個標準設定後,符合標準要求的,全球語言總字元數少於2的14次方個,16位綽綽有餘,因此Unicode 88標準非常慷慨,有一半的空間是留給用戶自定義和將來標準補充的,整個空間顯得特別空曠。這種標準提出後就經美國強力推廣,微軟用了這個標準寫了Windows NT的內核,蘋果就更不用說了,最著名的Unicode運動領導公司。
美國人的標準看似很有道理,事實上拉丁語系國家也不會覺得有什麼問題,但由於侵犯了東亞三國的利益,中國人,日本人和韓國人是無法接受的。在16位Unicode標準成為事實上的國際標準後(後面會提到)一位叫做Steven J. Searle的日本工程師針對這一問題寫過兩篇文章,我會附在後面,直斥美國的標準根本不配叫國際標準,而是傲慢的美國為自己產業量身定做的商業標準。其實Unicode一開始給了漢字兩萬多個字元,日常使用綽綽有餘,矛盾主要出在一些不太常用的字和古文中各種及其罕見的字要不要保留,中日韓三國同屬漢字文化圈,漢字能在通用的字元編碼中保留多少,直接關乎中華文明,日本文明和韓國文明的古代文化在電子信息時代中能不能被保留下來(字都打不出來還談什麼保存),但哪怕不考慮我們的民族主義,我也必須承認這個日本人的質疑十分尖銳而合理。美國人的吃相太難看了,在Unicode標準的綜述中,一邊說Unicode要成為通用標準編碼,一邊覺得32位太占空間了沒人會用所以強行上16位,但16位本來就是沒辦法容納所有字元的。提的那兩個字元篩選標準看上去還像那麼回事,但一深究就站不住腳。Unicode 88中對語言的重要性進行了排名,而排名的標準居然是國家的GNP,赤裸裸的看誰錢多就給誰面子。92年Unicode的1.1版居然只有24種語言,就是按照經濟實力排序在篩選哪些語言要被收錄的,直接無視了小國家的利益,也不知道Unicode的創始人怎麼敢說我統計了全世界所有的出版物結果全世界所有的出版物里只有24種語言文字。要不是因為中日韓三國GNP佔全球14%(88年)排第二,又占著地圖上根本沒辦法忽略的一大塊土地,美國人會不會給面子分給三國那麼大一塊空間很值得懷疑。另一個質疑則是文化霸權:憑什麼有用沒用是你來界定的?一個幾乎全是美國人組成的標準小組,也不是很懂中日韓三國文化,也沒有知會三國政府,就搞一個標準說我統一了你們的字元你們要聽我的?Unicode最初的字元標準中,很多人名和地名都打不出來要你何用?我是不是也可以統一英式英語和美式英語然後告訴你你要聽我的?最初的Unicode標準遭到很大的反對浪潮,特別是日本人當時正在和美國人在計算機標準上展開競爭(主要是東京大學主導的TRON開放計算機架構和微軟Windows的競爭,多語言字元編碼是其中的子項目)日本更不會容忍一個對本國有諸多缺陷的計算機通用標準
另一方面,自施樂公司和IBM開始搞多語言方案之後,瑞士日內瓦的國際標準組織ISO也開始跟進,也就是和美國平行的第二套多語言標準開發項目。作為一個組織,也許是因為不像美國的公司有迫切的經濟利益,設定標準的著眼點是不一樣的,應該是考慮到了16位不夠用的問題,ISO一開始就是以32位的字符集作為基礎,除非以後我們能遇到外星人,不然高達43億字元的容量肯定是夠用了。ISO這部分主要是日本和歐洲在主導開發,已經到了接近完成的程度。當然,世界上不需要兩個不兼容的編碼技術標準,更不要說這個標準本身就是致力於信息的統一交換的,這就涉及一個誰聽誰的的問題。美國畢竟是計算機大本營,IBM的通用PC在90年代初已經快統一市場了,上面跑的系統就是Unicode陣營的大戶微軟開發的,用的是16位的Unicode標準,當時沒落的蘋果則是Unicode的另一個大戶。美國如果執意對抗ISO,不理睬國際標準,ISO一點辦法都沒有,就會造成標準的分裂,甚至ISO可能會在標準爭奪戰中輸掉。因此92年,ISO妥協,放棄了最初32位的設想,同時廢除了日本和歐洲主導的32位通用編碼草案,採取美國的16位Unicode標準,這個標準最終被命名為Unicode 1.1或者ISO/IEC 10646-1: 1993。
但在實際使用過程中,16位一下就變得捉襟見肘,我猜是因為中日韓三國為代表各國反對(反對理由前文已經提了),因此這個標準被迫被「升級」,引入了代理對和文本平面的概念,技術細節略微複雜晦澀我們就不談了(為什麼文本平面是17個這種莫名其妙的數字我也沒找到答案),我們只要知道字符集從原先16位對應的UCS-2,被擴充到了32位的UCS-4(其實四個位元組占不滿,只是出於計算機的慣例用了4個位元組)。直觀來看似乎回到了ISO的預想,但是並非如此,ISO一開始設定的字符集是32位的,因此日本和歐洲就是按照原始32位設計的字符集,但是由於美國的Unicode是16位的,因此當時ISO採取美國的方案時ISO是直接廢掉了日本和歐洲的方案的,現在雖然看上去恢復成32位,但是為了Unicode向前保持兼容,只能在美國Unicode的基礎上再增加空間,而非回到日本和歐洲的標準。當然美國並非通吃,也還是做了讓步的,其中的很多相互妥協的技術細節我們也暫時不去管它(例如今天Unicode中文本平面這樣的概念源自於ISO標準的構想),我們只需要記住,最重要的節點是,一開始Unicode贏了ISO,因此Unicode這個16位的UCS-2字符集——美國的自定標準,最終還是成了ISO和IEEE承認的全球標準,但是後來發現Unicode的容量過小,因此進行了升級,最終還是使用了UCS-4,用32個位表示一個字元代碼。從字符集的大小角度來講,ISO的預判是合理的,美國是傲慢的或者短視的,但是從實際角度上來看,其實Unicode就是把ISO標準吃掉了。這也解釋了Unicode基本文本平面看起來很支離破碎的原因。例如,中日韓漢字由於要分批加入當然是可以理解的,但是漢字的編碼為什麼會支離破碎的分布在各種補充平面上?明明現在看來,Unicode的UCS-4是有足夠的空間的,為什麼不直接分一塊整的大空間給漢字?這是因為Unicode最初的版本就只給了漢字兩萬個空位,後面雖然升級了,但是最初的16位的空間已經滿了,所以只能在後面的補充平面加的到處都是。
其實沒有找到公開資料表明Unicode最終的拓展是中日韓漢字文化圈的抵制,但是憑藉邏輯推斷是合理的,Unicode 88標準很清楚的表示了Unicode就是美國的產業標準,裡面措辭的自負已經很清楚的表示出了制定者根本懶得管其它民族的文化傳承這回事,所以才敢出於經濟利益直接搞16位,如果不是中日韓三國(畢竟當年日本厲害,美國人總不可能推行一個世界標準,結果世界第二大市場拒絕使用)的反對,做出美國不會為非洲或者中亞不知道哪個小國家修改全球性的標準的推斷是合理的,那麼65536個字元下,很多文明的歷史,就徹底和電子信息時代say goodbye了。
Unicode與UTF從直覺上來看,我們覺得字元代碼和字元編碼應當是一樣的,在大多數情況下也的確如此,例如ASCII,因此其實長期以來我們混淆字元代碼和字元編碼概念時,也沒覺得有什麼不對。但是在Unicode里會有缺陷,Unicode字符集解決了通用的問題,但是如果依舊把字元代碼當作字元編碼來用,傳輸效率會非常低,Unicode本身的字符集是32位的,那麼每個字元都用4個位元組傳輸,對歐美而言,一下就要用原來ASCII碼時代四倍的資源傳輸或者保存一樣的文檔。對中國人而言也是一樣的,原先的GB雖然可能在打一些不常用字上有缺陷,但是也只要兩個位元組保存或傳輸一個字元,現在一下大一倍,是不可接受的。因此Unicode在推出之後推廣相當困難。直到互聯網時代的到來,跨語言信息交互空前頻繁,統一編碼大勢所趨,才被真正鋪開。耗費資源的問題最終也被解決,解決的方案就是UTF(事實上UTF中的UTF-8標準在93年就被提出,但是似乎很多程序員都表示這是在互聯網時代才興起的編碼方式)
UTF(Universal Transformation Format,通用傳輸格式),其實就是不改變字符集中各個字元的代碼,建立一套新的編碼方式,把字元的代碼通過這個編碼方式映射成傳輸時的編碼,最主要的任務就是在使用Unicode字符集保持通用性的同時節約流量和硬碟空間。主要分為UTF-8,UTF-16,UTF-32三種,數字表示每次傳輸,傳輸多少個位。此處會涉及定長碼和變長碼的概念,先做一個說明,定長碼的意思是一個字元編碼的長度是固定的,變長碼指不同字元佔據的編碼長度或許是不同的。定長碼由於字元長度固定,因此字元串的字元(有字元索引時)定位速度很快,因為位置是可以直接計算的,而變長碼由於字元長度不一,只能從頭開始解析所以會慢一些,但是相對來講節省空間。變長碼簡約空間,定長碼適合檢索解析,用哪一種並無優劣,只有權衡。
UTF-8編碼中,某些字元編碼是8位的(例如英語),某些編碼是16位的,某些編碼是24位(例如中文)。相當於用UTF-8在接收時,接到一個編碼如果是合法的8位編碼,就可以直接把它判定為字元,這就給了Unicode字符集在表示英語時和ASCII一樣的效率。如果是不合法的,那繼續讀一個位元組,讀的兩個位元組16位如果是合法的,判別為一個字元,再不行繼續讀下去,只要傳輸不出錯,終歸是能讀出的,利用UTF-8的編碼規則,這樣讀取不會出現錯判的情況(細節看UTF-8的編碼規則)。UTF-8這種編碼使得字元在實際傳輸過程中佔據編碼長度不一定是一樣的,因此相較於ASCII這樣所有字元長度一樣的定長碼,UTF-8的編碼稱之為變長碼。UTF-8大受歡迎的主要原因就是對歐美而言,在保留Unicode通用性的情況下避免了流量和空間的浪費。因此廣泛運用在互聯網,統計2016年互聯網87%的網頁是用UTF-8編碼的。
UTF-16編碼一樣,區別僅僅在於如果字元是在Unicode 1.1定義的那65536個點裡面(稱之為基本多語言平面),那麼只要一次讀取就會被判定合法,兩個位元組就可以判別字元。如果不在,那再讀一次,4個位元組一定可以解決問題。事實上,UTF-16本質上就是Unicode最早想要用的16位定長碼,無非Unicode標準的擴充,從UCS-2到UCS-4,使得它要加入機制來表示不在基本平面的字元,變成了變長碼。UTF-16也是非常常用的(雖然看上去像是美國強推自己Unicode時候定死16位,最後擴充到32位,但是為16位寫好的程序又改不了了產生的後遺症),Windows默認的Unicode編碼的保存方案,就是UTF-16。在保存數據方面,在保存英語上,UTF-16要兩倍於UTF-8的空間。在保存中文時,由於Unicode一開始加入的兩萬多個漢字基本已經涵蓋了所有的常用字,其實補充平面是極少用的,因此一般可以視為兩個位元組表示一個漢字,少數情況四個位元組表示一個漢字,因此效率相較UTF-8要高一些。UTF-32,位數和字符集大小一致,則很顯然能一次解析出字元,因此也是目前唯一的定長碼,但是空間浪費就很嚴重。
主要參考資料
Wikipedia, Unicode,Unicode
Wikipedia, UTF-8, UTF-8
Unicode 88, Joseph D. Becker, http://www.unicode.org/history/unicode88.pdf
A Brief History of Character Codes in North America, Europe, and East Asia, Steven J. Searle, Brief History of Character Codes in North America, Europe, and East Asia
Unicode Revisited, Steven J. Searle, Unicode Revisited
Wikipedia, 中日韓統一表意文字, https://zh.wikipedia.org/wiki/中日韓統一表意文字
知乎問題, Unicode和UTF-8有何區別 , 于洋的回答, Unicode 和 UTF-8 有何區別? - 編碼
因為utf8編碼的字元串長度和字元個數沒有固定換算關係,導致排版,排序之類的複雜度上升。
Go語言就是內部用utf8存儲,但它也提供rune類型來處理字元問題。首先,圖裡面說的 Unicode 是不準確的,它指的是Windows 常用的 UTF-16。UTF-16 是一種 Unicode 的編碼方式,UTF-8 是另一種 Unicode 的編碼方式。
其次,UTF-8 是一種不定長的Unicode 編碼方式,一個字元可能佔用1個位元組,也有可能佔用2,3,4 個位元組。
內存存儲位元組流的時候,字元不定長會給演算法帶來麻煩。比如你沒法確定第2000個字元在哪個位元組開始。
舉個例子:「ZH藥丸」 是一個四個字元的字元串
UTF-8 編碼是 "5A 48 e88daf e4b8b8"
UTF-16 編碼是 "005a 0048 836f 4e38" (這裡還有位元組序的問題,但是我們先忽略)
如果我想讓你找到第四個字元是啥,UTF-8 必須掃過整個 位元組流,而使用 UTF-16 的話,直接取出第四個16位整形(4e38)就好了。
(這是歷史,美好的時代已經過去,現在有些字元比如 emoji 也需要兩個wchar了,比如
,Unicode 是 」U0001f44d「, UTF-16編碼完了是是 」d83ddc4d「四個位元組。Unicode 經過擴充後,UTF-16 已經喪失了定長的優勢,但是Linux 4位元組 wchar_t 還是字元定長的)
(知乎估計用的是MySQL utf8,而不是 utf8mb4,竟然不支持 emoji 字元,只能貼圖了)但是我們存儲在硬碟上的字元串是不需要處理的,這時候通常情況下比較節約存儲空間的UTF-8 就成為了首選。 為什麼不少網站使用 UTF-8 編碼? - 互聯網
其實這個問題是不是:為啥程序內碼和外碼不能用相同的編碼方式?
(這裡有幾個概念,「字元編碼」、「字符集」、「編碼方式」等,因為懶窩就不說了,我先妄測題主的「Unicode」是指「Unicode 字符集定長的編碼方式」吧。)
籠統來說,內碼要求處理效率高,外碼要求傳輸方便。二者不可得兼的時候就會採用不同的編碼方式。
utf-8 沒有位元組序,所以用來作數據傳輸和存儲很方便。
定長的編碼在內存中可以快速訪問,所以用來作數據處理時效率高。
首先要把字符集(Charset)和字符集編碼(Character encoding)兩者的概念弄清
charset是一種字符集,它將抽象字元以數字編碼的方式存儲在集合中,常見的字符集有Unicode,ASCII等。
而encoding則是一種映射工具,它將自然語言的字元和字符集進行配對。常見的字符集編碼有UTF-8,GBK,Big5等。
然後我以Java為例講下具體的應用吧
Java編譯默認使用Unicode字符集,其中一個漢字兩個位元組,
char han = "漢";
然後默認使用的編碼是依據計算機系統的,如果是GBK,一個漢字正好也是兩個位元組。
但假如是UTF-8的話,那麼運行下面代碼就會看到一個「矛盾」
String str = "漢";
byte[] bytes = str.getBytes();
System.out.println(bytes.length);
結果會看到"漢"居然有三個位元組。
其實這三個位元組就是漢在UTF-8上的長度。
因為Windows就喜歡在內存里處理UTF16,當然不排除以後會升級成UTF-32。
「計算機」這個詞讓人糾結,題主得加些限制,比如說「裝了windows的計算機」,更詳細一點,還要說「win98以後的版本」,而且即使是高版本的windows,其實你也能自己攢出個不用unicode的記事本。說起來java程序更像一點,java的String運行時是unicode保存,平台無關,但是書上說的是內存。。。。我選擇不用String而用byte[]保存gb編碼行不行,誰又管得著?這書寫得好坑。
看錯題目了,重新補充在前面。
為什麼要用Unicode儲存,因為UTF-8是一種專門設計用來傳輸的編碼格式,是將Unicode字元串編碼成位元組的一種方式。
專門設計用來傳輸的編碼顯然不適合直接拿來表達字元串,一是字元串的長度無法預計,二是很難從中截斷或者截取字元。
但實際上Windows內部字元串並非真正是Unicode位碼儲存的,而是以UTF-16編碼格式儲存的。
這是因為以前以為16位足夠了,所以以前就是直接存的Unicode位碼,直到現在很多地方還是叫Unicode而不是UTF-16。
誰知道後來Unicode升位了,為了兼容,不能又把所有的字元用32位去存,就只好用UTF-16編碼代替,反正對於之前用到的那些字元,用UTF-16編碼存都是一樣的(低位Unicode字元的Unicode位碼和UTF-16是一樣的)。現在大多數書包括Windows文檔都還是會提到「Unicode編碼」這個詞,但事實上,說Unicode是一種編碼是帶誤導性的。應該說Unicode是一個「編碼標準」,它涉及到支持的抽象字符集、可用的編碼方式以及其他一些東西。
說它帶誤導性,一是因為以前採用的「UCS-2編碼」也曾經被稱為Unicode編碼,它是一種定長編碼,把每個字元映射為固定的16bit,但是現在Unicode標準收錄的字元已經遠遠超出16bit所能表示的範圍了,所以UCS-2已經被廢棄了;二是因為Windows文檔中對這個詞的錯誤使用,在Windows中所謂的「Unicode編碼」是指「UTF-16le」這種編碼方式,因為Windows一直都這樣寫,也就導致很多人包括很多寫書的人沿用了這樣的說法。
前面很多答者也提到了。在內存中使用定長的編碼可能會有更優的性能,具體用什麼編碼不是一定的,看怎麼實現而已。而傳輸和存儲要節省空間的話,當然就採用可變長度的UTF-8更好了,畢竟要是英文字元全都用16bit來表示的話,額外開銷會非常大。
想了解多一點的話,不妨點進我之前寫的一篇總結性文章:字符集與編碼的恩怨情仇。感謝各位知友共享知識的精神。你的圖和你問題是2個事情。?
你的圖,關於記事本的的那個圖。
Windows (NT系列)內部採用的是UTF16編碼(表示,存儲格式)。所以就有了你這個圖,其實他轉換的目標所謂的Unicode,其實是UTF16編碼。
所以打開,保存都需要進行轉換。
為什麼Windows 選擇UTF16做編碼,而不選擇UTF8?主要還是歷史原因了。
其實這些誤解和大家對名稱的誤用有很大關係。
UTF16因為是固定長度字元,所以處理起來簡單高效,缺點是浪費存儲。UTF8對空間利用很高效,所以網路傳輸裡面經常使用UTF8作為標準,缺點是因為變長處理比較複雜。
兩者各有利弊,要針對應用在時間和空間之間做權衡。我感覺Windows在這點的設計上面突然激進了一下。UTF16很優雅,有一定的道理,但是這麼多年了似乎還是沒有成為主流。
反正在Windows上一定要注意UTF16是個大坑需要特殊處理。很多人根本弄不清楚GBK,code page,UTF16,UTF8這些概念,我建議一定要弄清楚,不管你是否需要處理本地化。否則會經常掉進開源庫、以及各種工具的大坑裡面。「計算機機內存中,統一使用Unicode編碼,需要保存或者傳輸時,轉換成UTF8編碼」 這話是不對的。應用程序或操作系統或語言編譯器或虛擬機它們內部採用什麼標準的編碼來存儲和處理字元是它們自己的事,按自己方便、需要和意圖而定,並沒有一個統一的要求。
常見的應用和系統內部採用unicode,因為unicode包容性最好嘛。保存或傳輸也是一種「處理」,這時採用什麼編碼,完全取決於具體需要。
如此而已。
因為當時設計的時候還沒有UTF-8.
前面有些答主說的不一定對. UTF-8的問題確實在於不定長, 即字元數和位元組數沒有明確的換算關係, 但是這個問題不止是UTF-8, 對於UTF-16來說也是一樣的(某些字元是4個位元組的). 只有對於UTF-32來說才是明確的一個字元4個位元組的換算方式(但是顯然這種方式太浪費空間).
現在很多系統內部的存儲都是使用UTF-16的很大程度上是歷史原因. 比如Java之所以選擇UTF-16作為內部存儲編碼只是因為在當時已經有了UTF-16 (當時還叫Unicode), 而UTF-8標準當時還沒有出現. 直到後來發現不夠只能擴展, 才導致UTF-16的定長特性被破壞. 如果重新選擇編碼的話, 我想大多數系統都會使用UTF-8來存儲的. 比如現在的Ruby的標準實現中就直接用UTF-8存儲字元串了.linux和mac就是完全用的utf8,只有windows不是。
因為utf8出現得比utf16晚,utf16先入為主了,windows內部已經設計成使用utf16了。
後來發現utf16的局限性,utf8才是最完美的,但是兩者差別有點大,讓windows內部改成utf8估計有難度,可能影響到現存的很多程序,所以windows內部依然是utf16,而linux歷史包袱沒那麼嚴重,所以內部已經採用utf8了,這樣就不需要讀取和保存的時候轉換了,格式大統一。mac系統應該也是utf8。
另外,windows保存不一定是utf8格式,跟具體的應用程序有關。
這個問題我是這麼想的:
Unicode用於處理字元以及表示,存儲時則用UTF進行格式轉換到二進位。
Unicode只是給字元指定了一個唯一的數字編號,這個編號是以 U+ 開頭的十六進位數字,但是Unicode並沒有規定這個字元在計算機中對應的二進位是什麼。
將Unicode的編號轉換到二進位的是UTF(Unicode Transformation Format)。
UTF-8/UTF-16/UTF-32都是為了把Unicode對應到二進位形式(位元組),只是不同的實現方式(具體挺複雜的,自己也沒多清楚就不瞎說了)
先問是不是,再問為什麼。很多linux發行版都用utf8作為默認編碼的。utf8和utf16各有優缺點,選擇哪個作為默認編碼,作為設計過程中的tradeoff都是正常的。
Unicode標準並沒有規定應當使用哪種編碼方式,開發者可以自行選擇。
1988年,發布了內部初稿Unicode 88。
1. 早期只使用16位定長編碼並且不考慮兼容ASCII,沒有UTF-16/UTF-8等差異。於是開發者往往自稱支持「Unicode編碼」。(UCS-2)
2. X/Open決定發明一種兼容ASCII的編碼方式,在1993年USENIX會議上確定為「UTF-8」。獲得Unix業界大力支持。
3. 1996年Unicode發布2.0標準,碼位空間增加到32位。以前的16位編碼方式UCS-2增加變長功能後改稱UTF-16。
4. 多家大型企業在UTF-8推出之前就把內碼確定了,再改動API會破壞兼容性。
延伸閱讀:www.unicode.org/faq/utf_bom.html
www.unicode.org/versions/enumeratedversions.html
www.cl.cam.ac.uk/~mgk25/unicode.html
此文錯誤使用大量名詞,所謂計算機內存統一使用Unicode編碼指Windows API大量偏好UTF-16 LE編碼的字元串作為參數(也存在老舊API接受其他編碼的字元串參數,但是不推薦使用,僅作為兼容性支撐而存在),且Windows自帶的notepad.exe軟體將UTF-16 LE錯誤的稱為Unicode編碼。
實際上所謂「計算機內存」是啥,「統一使用的編碼」又是啥(實際上啥都不是,不存在的),其他答案扯淡一大堆,這你都不需要知道。你只要理解這文章所述的意思的就好。
而存儲文本文件或用於傳輸字元串數據時,常用的技術是轉換為UTF-8編碼以節省存儲空間和寬頻。不直接使用是因為Windows開發團隊認為UTF-16 LE作為Windows API字元串參數的編碼更好(浪費空間但是有其他好處,你不需要明白),一種選擇而已,如同選擇買貴的更好的還是便宜的能用的生活用品一樣的原因,多因素考慮後折衷的決定,僅此而已。
廖雪峰 python教程,朋友哇 !
推薦閱讀:
※Unicode 是不是只有兩個位元組,為什麼能表示超過 65536 個字元?
※為什麼編碼(GBK、Big-5 等)問題這種歷史遺留始終得不到解決?
※如何用 Potplayer 消除 .srt 文件的中文字幕亂碼?
※如何評價 iOS 8.3 中各種膚色的 Emoji 表情?
TAG:程序員 | 計算機 | Unicode統一碼 | 字元編碼 | 字符集 |