ASCII 、GB2312、GBK、UTF-8 編碼:
原文地址:http://hi.baidu.com/phpease/item/f450b5caec143014505058fc
變數命名規則:
1. 變數名區分大小寫(case-sensitive) 。
2. 必須字母或底線開頭。變數名可由 字母、數字、底線組成。
看到這裡可能,很多人納悶了~。那為啥 $我是變數 這樣的中文也能做變數名呢。 在PHP裡,中文的確是可以做變數名的(能用是能用但千萬別項目上用....) 。 因為這裡的字母是指: a-z A-Z 和 擴充ASCII 字元裡從 127 到 255 ,16進位表示為:0x7f-0xff 。 那意思就是ASCII字元裡從127到255 (0x7f-0xff) 涵括了中文。 的確是這樣的。下面就簡單的講下編碼。
ASCII 、GB2312、GBK、UTF-8 編碼:
ASCII :
ASCII 編碼裡包括了128個字元。用 十進位 0 到 127 來表示 。那就對了, 0 到 127 不就是 128個字元嗎。 每一個數字都代表一個字元。看ASCII 編碼錶
我們先看十進位(Dec) 這列,看到了嗎 。0對應於null(字串的末尾分隔字元\0) ,十進位數字 9 對應 的字元是 我們開發用得最多的 TAB鍵。 再看看 48 對應的字元是 0 。沒錯從 ASCII 48開始 到57 都表示數字 0到9 。 ASCII 97 到 122 表示 小寫字母 a 到 z 。 比如: 我們看到的字母 a ,其實電腦並不認識啥 字母a , 她只認識 97。 她把a 轉成了 ASCII 97來進行儲存。下面用PHP 來玩玩 ASCII 。
來我們認識兩個函數:
ord ---- Return ASCII value of character 返回 字元竄的 ASCII 值 。
chr ---- Return a specific character 返回 ASCII 對應的 字元。
翠花上例子:
<?php echo(ord('a'));?>
//輸出 97
沒錯吧, 小寫a的 ASCII 就是 97。 要把 ASCII 97 對應的字元列印出來:
<?php echo(chr(97));?>
//輸出 a
恩。看完基本就明白了。 ASCII 編碼 裡麵包括了 大小寫字母 數字 和 一些常用的控制字元。 這樣在使用英語的國家基本就能使用了。電腦儲存的是ASCII 。 人看到的就是 ASCII 對應的字元。
GB2312 編碼:
世界上並不全是用英語作為語言的。比如我國用的是中文。小日本用的是 日語。 韓國用的是韓文。 這些語言和英語完全不是一回事。 你看ASCII 表上有中文對應的”數字“嗎。沒有吧。因為還有一份 GB2312編碼錶 ,和ASCII 編碼錶道理一樣。 連結: http://wenku.baidu.com/view/244e2d2ce2bd960590c677a6.html 大家開啟一看,哎呀~~。 是否有點亂亂,找不到頭緒,啥亂七八糟。 不過當明白原理,就容易看懂了。
在GB2312編碼裡面,一個字元我們需要用兩個位元組來進行儲存和表示。我們記得ASCII 編碼裡面 一個字元只需要一個位元組。所以以GB2312儲存資料比 ASCII 大一倍。 那麼GB2312 這兩個位元組,分別放啥數字 才能表示字母 a 呢。 我們知道 ASCII 編碼 a 就一個位元組表示,編碼97。 GB2312 編碼比 ASCII 複雜一點,
在要看懂 GB2312編碼錶之前,首先我們要學習下”區位碼“。
區位碼概念:
GB2312對漢字和其他字元(字母,數字等)進行了“分區”。
01-09區為特殊符號(數字呀、字母呀等)。
16-55區為一級漢字,按拼音排序。
56-87區為二級漢字,按部首/筆畫排序。
分區是啥。比如我是廣西的,你是河南的、他是廣東。 也就是說每個字元肯定存在於某個區裡。 這種表示方式叫做 “區位碼”。 區位碼 其實是 區號和位號(表示一個字元在這個哪個區裡的第幾列) 。 你想知道每個漢字的區位碼。簡單呀。給你個連結自己查去 http://www.jscj.com/index/gb2312.php
我們以 “啊” 這個漢字來查它的區位碼。 以上圖所示 1601 是 “啊”這個漢字的區位碼。 區號是 16 , 位號 01。 如果大家還記得, 16區 是 一級漢字哦。牛X呀。一級漢字是啥意思。 我估計是常用的漢字。~我也不知道,這個是國內專家定義的。 位號 是 01 ,位號其實就是說你在這個區裡排行老幾。 有了 x軸(區號)和y軸(位號) 那麼自然就又個交點,通過交點就能在 GB2312編碼錶上找到對應的漢字了。
如果你現在就去看 GB2312的編碼錶,我估計你還是看不懂,雖然說通過區位碼就能定位到GB2312編碼的字元了。但是GB2312編碼錶上並沒那麼單純。 還要繼續往下看。
上面我們說的是GB2312的 區位碼: 區號和位號。 前面我說過,一個GB2312的字元 是用兩個位元組來表示的:(高位位元組,低位位元組)。第一個位元組稱為“高位位元組” ,第二個位元組稱為“低位位元組” ps:因為高的一般排左邊吧~ 所以叫 高位位元組.... 。
演算法如下: 一個GB2312的字元 == (0xA0 + 區號,0xA0 + 位號)。按照這個演算法,你再取看 GB2312編碼錶,你就看得如魚得水了。
0xA0 是啥意思呢。 為啥高位位元組 等於 區號 加上 0xA0 。 為啥低位位元組 等於 位號 加上 0xA0呢。這樣組合起來的兩個位元組就能表示一個 GB2312的字元。 沒錯,就是那麼簡單。 0xA0 是一個16進位數字 換算成 十進位其實就等於 160 。 高位位元組 等於 160 加上 區號,你可以理解為,其實就是 GB2312編碼字元 是從 160 起步的。 就好比 ASCII 編碼是從 0開始 到 127 結束。
我們重新來看下演算法 : 一個GB2312的字元 == (0xA0 + 區號,0xA0 + 位號) 。
以上所看,只要我們知道 區位碼(區號和位號)就能算出一個漢字的GB2312編碼數字。 字母a 的區位碼 : 0365 ,也就是區號 03 位號 65 。 按照上面演算法我們算下。 把 0xA0 換成十進位 等於 160。 也就是 (160+03,160+65) 等於 (163,225) 換成 16進位(編碼錶一般都是16進位) (A3,E1) 。 OK了。 字母a 的gb2312編碼 出來了,拿著 A3E1去 GB2312編碼錶去找這個16進位數字對應的字元吧。 如果你沒看錯的話,沒錯就是對應著 編碼錶上的字母 a 。
所以, 只要記得 上面公式, 找個工具算出漢字區位碼,然後套進公式裡面算下。就能的到這個字元的GB2312編碼值了。 大家可以自己動手去試試算出上面的漢字 “啊” 的GB2312的編碼值。
小總結一下,大家記住:
ASCII 編碼的範圍 -- 十進位 => 0 - 127 。 十六進位: 0x00 - 0x7F 。
GB2312編碼的範圍 -- 十進位 => 高位位元組:161 - 247 。十六進位:0xA1 - 0xF7 , 低位位元組: 161 - 254 。十六進位:0xA1 - 0xFE 。
GBK 編碼:
GB2312 之上的一種擴充編碼,GBK 編碼已經包括了GB2312編碼,並擴充了GB2312編碼,使它能表示更多的字元。 GB2312和GBK 原理一樣,他們區別只是,編碼值範圍不一樣了。 GBK 更大了。
GB2312 編碼值範圍 : 高位元組從A1到F7,而低位位元組從A1到FE。
GBK 編碼值範圍: 高位元組從81到FE,而低位位元組從40到FE 。
以上範圍可以看出。GBK 比 GB2312大很多。 大是大了很多... 不過現在一般項目都用UTF-8編碼了。 接下來將下UTF-8編碼方式
UTF-8編碼:
世界上那麼多國家,每個國家的語言都不一樣。一會出個 ASCII 一會出個 GBK 一會出個 XXOO 編碼。那崩潰了。是否能發明一種編碼方式,能很好的表示出所有語言呢。 Unicode編碼就是這樣產生的。這裡我們只講Unicode中得一種實現方式。UTF-8,當然還有其他的實現方式。但對於我們WEB開發來說,並不常用。
ASCII 編碼能很好的表示字母、數字等。所以UTF-8 就在它的基礎上進行了一下擴充。 按照慣例,我們還是先看下 Unicode編碼 表(UTF8編碼錶。木有。我們需要掌握如何從Unicode 轉換成 utf8)
學習這節的目的
掌握從Unicode 轉換成utf8編碼的方法 判斷UTF-8下的字元的位元組數。 看下錶:
unicode 位元組位表
unicode 編碼範圍 十進位/十六進位
UTF-8 位元組模板二進位/十六進位
位元組數
(0)000000 – (127)00007F
0xxxxxxx(00-7F)
一位元組
(128)000080 – (2047)0007FF
110xxxxx(C2-DF) 10xxxxxx
兩個位元組
(2048)000800 – (55295)00D7FF (57344)00E000 – (65535)00FFFF
1110xxxx(E0-EF) 10xxxxxx 10xxxxxx
三個位元組
(65536)010000 – (1114111)10FFFF
11110xxx(F0-F4) 10xxxxxx 10xxxxxx 10xxxxxx
四個位元組
這個表很重要,記下這個表就基本瞭解了UTF-8 是怎麼一回事。 UTF-8 一共能用四個位元組來表示。 但一般字元呢基本用三個位元組就能滿足了。
一個位元組等於8位。這個大家都知道。 從 00000000 - 11111111 這個就是一個位元組的數值範圍。 換算成十進位就是 0 - 255 。 懂了這個我們繼續往下講。
繼續看上圖, 我們慢慢講:
UTF-8中之 一位元組:
在UTF-8裡面對ASCII 編碼進行了保留然後再它之上進行了擴充補充。 一個位元組 存的 還是字母呀 數字呀 和ASCII 編碼一樣。所以 編碼範圍也是 0 - 127 。
有點同學納悶為啥是 127 呢。 一個位元組換算成二進位不是 255嗎。 因為一位元組的時候,第一位給借去了,第一位的值為 固定為0 。大家看上圖第一行 “UTF-8位元組模板“ 這一列 就明白了。所以其實只有7位是用來表示字元。那麼 換算了下 7位 的二進位 就只有 0 - 127 了。 這個幾乎和ASCII 編碼一樣, 想知道0 - 127 都分別對應了什麼字元。 看ASCII 編碼錶呀。
UTF-8 之兩位元組:
一個位元組 8位,兩個位元組就16位了。哇。值更大了,能表示的字元更多了。所以什麼希臘字母呀、拉丁字母呀等都可以用兩位元組來表示了。 看 第二行的 ”UTF-8位元組模板“ 這一列 。110xxxxx 10xxxxxx 一共有16位, 每8位一個位元組。 大家知道,在一個位元組的時候,第一位是不能用的。 兩個位元組的時候稍微不同了。 在兩個位元組的時候, 第一個位元組的前三位給借去了,同時第二個位元組的前兩位也是給借去了。 恩在這裡,我們只要明白一個地方就行。 UTF-8 編碼中 當字元是兩個位元組表示的時候,第一個位元組的編碼值範圍是多少。 第一個位元組是 : 110xxxxx 。 那麼也就是範圍從 11000000 - 11011111 換成十六進位範圍是 C2 - DF 。 恩懂這一點,就足夠了。 以後遇到 寫 UTF-8編碼 下的 截取 函數 、統計長度函數 就不用怕了。
UTF-8 之三位元組:
三個位元組表示,是我們用的最多的,因為俺們寫中文的嘛。 不過這裡注意下就是 ,三位元組下的借位情況。 繼續看上圖 。 1110xxxx(E0-EF) 10xxxxxx 10xxxxxx 看到了嗎。你懂的~ 如果還不懂...還是繼續重頭看起吧。 UTF-8 下 一個字元三位元組的。 第一個位元組 的範圍是多少? 這個必須弄清楚。 範圍是從 11100000 - 11101111 十六進位是: E0 - EF 。
UTF-8 之四位元組:
這個遇到真不多。 不過道理 你真懂了。我就不說了
好了。我們完成了一個目標了 : 判斷UTF-8下的字元的位元組數。比如以後開發你遇到:
對於這段文字 : "逆雪寒之PHP拾遺" 。 我要在UTF-8下統計它的字元長度 和 實現截取字元竄。 應該沒那麼心慌了。 當然有人說,統計字元長度和截取中文字元竄不是很簡單嗎。mb_strlen 、 mb_substr 。 的確是可以呀。 但我想我們要知其然知其所以然 。 我們的目標是 PHP產品級研發。 不是 PHP企業網站級研發 -_-! 。
接下來完成另外一個目標: 掌握從Unicode 轉換成utf8編碼的方法
我們繼續看 unicode 位元組位表 。 看第一列 unicode 編碼範圍 。 四個位元組,所以就有四個範圍 。 看這個 (0)000000 – (127)00007F 十進位從 0 開始到 127 。這個就是第一位元組的unicode 範圍。 其他的也是同一個意思。
明白了上面講的以後,現在開始講 unicode 編碼轉換 UTF-8的流程:
我們用 “啊” 這個漢字為例,它的 Unicode 編碼是 U+554A (怎麼知道的。查 unicode 編碼錶呀 大哥...) 。 然後我們轉成UTF-8 :
U+554A 換成十進位是 21834 。 比對 上面的 unicode 位元組位表 的第一列 。看到 21834 是在 三個位元組的 (2048)000800 – (55295)00D7FF 範圍之內。 因為 “啊” 在UTF-8 裡是三個位元組的。
三個位元組 的UTF-8模板 是(看 unicode 位元組位表 ) 1110xxxx 10xxxxxx 10xxxxxx 。
"啊“ 的 U+554A換算成二進位是 : 101 010101 001010
把15位二進位按照順序的填入(不足最後補0) 三位元組的 UTF-8 模板裡面 。也就是 11100101 10010101 10001010 。 第一位元組 不足位,所以最前位補0.
最後結果 ,0xE5 0x95 0x8A 這三個就是 ”啊“ 字的UTF-8編碼了。
<?php
$v1='E5';
$v2='95';
$v3='8A';
$v1=base_convert($v1, 16, 2);
$v2=base_convert($v2, 16, 2);
$v3=base_convert($v3, 16, 2);
echo $v1.$v2.$v3."************<br>";
$sss=$v1.$v2.$v3; 111001011001010110001010
?>
0101010101001010****21834************啊