小白在學習 python 中的「字串」這個基本概念時,問了大神這樣乙個問題:既然 python 內部的字串都是使用 unicode 來編碼的,那幹嘛還要在儲存和傳輸的時候轉成 utf-8 呢?這樣轉來轉去的多麻煩?
說實話,回答小白的問題,通常比回答大神的問題要難。因為他們總是問一些不著邊際的問題。好在偉大的網際網路時代,沒有什麼事情不可以通過在網上蒐集資訊,整理分析之後給出乙個相對滿意的回答的。
小白提出這個問題,主要是源於網上教程中乙個錯誤的解釋:unicode標準也在不斷發展,但最常用的是用兩個位元組表示乙個字元(如果要用到非常偏僻的字元,就需要4個位元組)。於是,小白就認為,既然2個位元組可以搞定,幹嘛還搞成 utf-8,因為那樣可能需要 3 個位元組或者更多來表示乙個字。這裡的錯誤是什麼呢?就是混淆了「標準」和「實現」。好吧,小白肯定又要暈了。
先來舉乙個現實中的例子:現在我們使用的長度單位是「公尺」。這就是乙個標準。這個標準怎麼定義的呢?在國際計量學會有幾個標準「公尺」容器,那個東西的長度,就被定義成乙個標準的「公尺」。但不會有誰真的跑去用那個標準容器。我們都是以那個標準容器為樣板,生產自己的尺,來測量東西。這個尺就是「公尺」這個標準的一種實現。我們可以做一根硬的木尺,也可以是軟的捲尺,還可以是使用雷射測距的光尺。不管怎麼說,只要這個尺量出來的「公尺」和標準容器量出來的「公尺」是一樣的,那就可以了。
那麼 unicode 是怎麼回事呢?unicode 做為乙個國際標準,其實只是定義了每個字元對應的乙個數字(實際上 unicode 包含很多複雜的內容,這裡只說最簡單的部分),比如 20031 這個數字就代表「中」這個字,25991 代表「文」這個字。但其實 unicode 並沒有說你要怎麼儲存乙個字。
講這麼一堆關於怎麼表示乙個數字的問題幹什麼呢?和這裡說的 unicode 和 utf-8 有什麼關係呢?當然有關係!這裡大神要說的意思就是:同樣乙個數,你可以選不同的方式來表示它(比如剛才說的用3個位元組來表示正負數,或者使用符號位來表示正負數)。選用什麼方案,就是對 unicode 的一種編碼方法,也就是我們剛才說的「實現」。為什麼前面提到的教程作者會說 unicode 通常是兩個位元組呢?因為在 unicode 早期,設計 unicode 的人學識有限。他們在考查了世界上主要的文字之後,感覺用2個位元組也就是最多 65536 個字元應該夠用了。對應於一種 2 位元組的編碼方案叫 ucs2。後來做著做著,發現不對了,2個位元組不夠用了(現在 unicode 包含了 12萬 8 千個字元),又擴充套件到4個位元組(因為計算機使用二進位制的原因,3個位元組在讀寫的時候很沒效率,所以都是2的倍數來擴充套件的),對應於一種編碼方案叫 ucs4。而對於我們來說,常用的中文字都在 65535 以下,所以通常 ucs2 就能表示常用的 6000 多個漢字了。這裡說的 ucs2 和 ucs4 都是 unicode 的「實現」,或者叫編碼方案。雖然,因為 ucs2 不能覆蓋全部的 unicode 字符集,已經被標為「廢棄」了,但不要被他們騙了,乙個已經使用了很久的技術,想廢棄是非常難的。
ucs2 和 ucs4 都是固定編碼長度的方案。也就是說不管是什麼字元,都使用 2 個位元組或者 4 個位元組來表示。對於我們來說,這並不是什麼問題,但問題出在:這個世界上最流行的語言不是漢語,而是英語(中國人表示不服!漢語是世界上使用人數最多的語言)。雖然把每個英語字母都使用兩個位元組來表示也不是什麼大的問題,可是,在遙遠的計算機啟蒙時代,無論是硬碟的大小還是網路的速度,都非常的有限,如果不對傳輸的內容進行壓縮和限制,那成本是非常高的。於是就有了 utf-8。
和 ucs2 這樣的固定編碼長度的方案不同的的是,utf-8 並不是固定長度的。最短的 utf-8 字元只需要使用 1 個位元組。最長是 5 個位元組。最長那 5 個位元組能表示的數字,相當於用 4 個連續位元組用來表示正負數,裡面正數的個數,大概有20多億,具體是多少,其實也不重要。看起來,多用了乙個位元組,還沒有原來表示的數字多,好像很浪費,但這裡的目的是:最常用的英文本母,使用最少的位元組,不常用的,反正也不常用,多兩個位元組沒關係。(這裡請不要批判帝國主義霸權。不是人家想把英文搞這麼短,是我們的中文想搞短也做不到……)
說回 python。在 python 3.3 版之後,對內部的 str 表示做了一些改進。教程裡說:python 內部使用 unicode 來表示字串,而在儲存到硬碟或者傳送到網上,需要轉成 utf-8。這個說法其實不夠準確。在 python 內部(不同的版本可能不同,大神在這裡引用的是 python 3 的文件),是使用 ucs 的各種版本來表示字串的。比如說,對整個字串做一次掃瞄,發現所有的字元都小於 255,那麼就使用 ucs1,也就是乙個位元組的編碼方案;如果發現有大於 255 的字元,但又都小於 65535,那麼就使用 ucs2。當然,如果發現有奇怪的字元,那就只能使用 ucs4 了。為什麼在內部要使用 ucs 呢?怎麼不繼續使用 utf-8,那樣不就不用轉換了嗎?(小白總是想省事)這個就涉及到 utf-8 的乙個缺點:計算字串長度和查詢子字串非常沒效率。在使用 ucs2 的時候,要想知道這個字串有多長,只要看一下它佔了幾個位元組,然後除個 2 就可以了,而 utf-8 的話,就需要乙個字元乙個字元的數出來。在做子字串搜尋的時候,因為不知道下乙個字元佔幾個位元組,所以那些高效的搜尋演算法也都不靈了(小白:演算法是啥?)。基於這兩個原因,只好做乙個轉換:在儲存到硬碟或網路傳輸的時候,使用一種壓縮的方案,使得傳輸或儲存需要的位元組數最少;而在內部進行處理的時候,則使用效率更高的 ucs 方案。
ps:大神非常爽的講完之後,小白似懂非懂的走
為什麼要採用UTF 8編碼
前不久有個問我說修改l blog時老是出現亂碼,好像和其他的中文編碼不一樣,我說因為採用了utf 8編碼。現在大多數程式 bbs blog 都是基於gb2312編碼的,但是為什麼要改用相容性不好 容易亂碼 的utf 8呢?開始我也不了解,直到我看了zola的這片文章才有所了解 中文有三種字符集,統一...
MySql千萬不要用utf 8!!
錯誤 incorrect string value xf0 x9f x98 x83 for column xx at row 1 原因為 utf8 只支援每個字元最多三個位元組,而真正的 utf 8 是每個字元最多四個位元組。mysql 一直沒有修復這個 bug,他們在 2010 年發布了乙個叫作 ...
utf8 和 UTF 8 有什麼區別
utf 8 是標準寫法,在windows下邊英文不區分大小寫,所以也可以寫成 utf 8 utf 8 也可以把中間的 省略,寫成 utf8 一般程式都能識別,但也有例外 如下文 為了嚴格一點,最好用標準的大寫 utf 8 在mysql資料庫中只能使用 utf8 在mysql的命令模式中只能使用 ut...