關於字元編碼

ASCII碼錶
作為一名程式員,對編碼問題有必要了解一下。計算機是以二進位制形式儲存資料的,至於為什麼選擇二進位制,這可能就需要從計算機的誕生歷史說起了,這不是現在關注的點。
計算機檔案一般可以分為(暫且這樣分吧):文字檔案和二進位制檔案。通俗的說,文字檔案就是平常通過某個軟體(記事本,EditPlus,UltraEdit,sublime等各種編輯器,各種IDE等等)開啟看到是‘字元’,二進位制檔案就是開啟是圖片或視訊等,當然是通過某些能夠解釋二進位制的程式來完成。
這裡需要強調的是,檔案的字尾名不能用來區分檔案的型別,字尾名只是系統(特別是Windows)用來對檔案進行預先分類,並給予對應的圖示和相應雙擊後開啟的程式,至於預先給定的程式能不能開啟就不得而知。
如,在Windows下,把一個txt檔案的字尾名改成jpg後:

image
變成

image
雙擊打不開,用記事本依然能開啟,就是內部編碼沒變,其實檔案的型別在編碼成二進位制形式已經確定(一般是在頭部)。
php讀取圖片檔案:
$a = file_get_contents('bg.png'); echo $a;
瀏覽器以UTF8形式開啟圖片的二進位制形式,雖然大部分是亂碼,但頭部還是可以看到解碼為PNG的:

字元編碼
字元編碼就是每個在電腦裡出現的字元都會對應一個二進位制數,這個二進位制可以成為 碼點 (code point)。不同的編碼形式,包含的字元數不同,字元和二進位制數對應關係也不同。
ASCII碼
ofollow,noindex">ASCII (American Standard Code for Information Interchange)碼是 比較早的編碼形式,總共定義128個字元,用了一個位元組的7位,也就是從 0000 0000
到 0111 1111
。
-
0-32, 127
:(共34個)不能直接顯示字元的(控制或者通訊使用的),32是空格也算在其中,127是刪除符; -
33-64,91-96,123-126
:(共21*2=42
個)鍵盤上除了英文字母和不直接顯示字元的鍵有21個,每一個鍵上有兩個字元; -
65-90,97-122
:(26*2=56
個)英文字母;
對於英語系的人來說這128個字元已經夠了,他們在電腦上接觸的的字元也就這麼多夠了,說來也是奇特,英文字元26個就能通過組合用來表示英語所有意思,這種組合形式很適合計算機。而我們漢語就不同了,我想是不能通過筆畫的組合組成所有漢字了。
據說漢語是最難學的語言之一:sweat_smile:,辛虧我已經會漢語了:grinning::stuck_out_tongue_closed_eyes:。
Unicode
128個字元對於其他語言是遠遠不夠的,單漢字就有將近10萬個(常用應該幾千個),每個漢字都需要一個碼點,其他語言中的字元也需要對其字元編碼成碼點,原本有很多編碼方式,這些編碼沒有進行統一規定,就會有衝突,一個二進位制數在不同編碼方式中就有可能解釋為不同的字元,這樣使不同地區的人交流不便。
Unicode就是在這樣條件下誕生,簡單的說,Unicode就是大的對映表,把全世界所有語言符號都包含其中,每個符號的對映的碼點的都不相同。 漢字大部分可以用4個16進位制數表示,參看 漢字Unicode 。如: U+620E
表示 戎
(這是我的姓,讀 róng ,不是 戒
,在這裡普及一下,我已經被叫成 戒某 無數次了 ♀️ ♀️ ♀️)。
Unicode是一個龐大的字符集,一個很多字元與二進位制數的唯一的一一對應集合。Unicode沒有規定怎麼儲存, 620E(0110 0010 0000 1110)
至少需要2個位元組,而其他字元可能需要更多位元組。像 U+0041
表示英文字母A,如果也需要用2個位元組或者更多,則前面的有一些位元組都是0,那麼太浪費資源了,為了減少空間的浪費就出現了UTF8。
UTF8
UTF8是Unicode儲存一種實現方式。它採用 變位元組數 (1-4個)來節約資源。 UTF-8的編碼規則很簡單,只有二條:
- 對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的Unicode碼。因此對於英語字母,此時UTF-8編碼和ASCII碼是相同的。
- 對於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 |
這樣解讀UTF8編碼就容易區分幾個位元組表示一個字元了。如果一個位元組的第一位是0,那麼這個位元組單獨表示一個字元;如果一個位元組的第一位是1,那麼這個位元組下面連續幾個1,就表示當前字元佔用幾個位元組。
以漢字 戎
為例,看看UTF8編碼實現過程: 已知 戎
的Unicode是 620E
(0110 0010 0000 1110)。
-
620E
在第三行範圍內(0000 0800 - 0000 FFFF)
,因此需要三個位元組編碼戎
,即1110xxxx 10xxxxxx 10xxxxxx
- 從
戎
的最後一個二進位制位開始,依次從後向前填入格式中的x,多出的位補0。得戎
的UTF8編碼的十六進位制是E6888E
。

image