【原創乾貨】造成網頁亂碼的根本性原因,乾貨根本性
先看段代碼:
<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>網頁編碼</title></head><body></body></html>
HTML代碼中的<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 指定了網頁的編碼為utf-8。
網頁編碼涉及的知識點比較多,總的說來它也是一個曆史遺留問題。
第一台電腦(ENIAC)於1946年2月誕生於美國,當時美國只考慮自己使用,並在電腦誕生後的幾年裡制定了一套ASCII碼標準(American Standard Code for Information Interchange,美國資訊交換標準代碼),它是基於拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其他西歐語言。
ASCII碼使用8位位元組合來表示256種可能的字元(2的8次方=256),包含了大小寫字母,數字0到9,標點符號,以及在美式英語中使用的特殊控制字元。一個字元佔1個位元組。ASCII碼錶部分編碼如下:
HTML的轉義符(字元實體),比如符號“<”的轉義符為“<”或“<”,其中的數字編號“60”即是ASCII碼錶的第60序號。類似的,大寫字母“K”也可以轉義為“K”。
我們使用轉義符做個實驗:
美國制定ASCII碼的意思是:ASCII碼可以滿足在電腦領域所有字元和表示上的需要。不過這隻是美國自己的意思,畢竟所有的英文單詞都可以拆分來自26個英文字母,ASCII碼錶能表達256個字元,確實足夠美國使用。
後來世界各地也都開始使用電腦,很多國家的語言文字並不是英文,這些國家的文字都沒被包含在ASCII碼錶裡。以我們中國為例,漢字近10萬個,根本無法排進ASCII碼錶。於是我們國家對ASCII碼錶進行拓展並形成自己的的一套標準,在標準中一個漢字佔2個位元組,新的碼錶可以表達65536個漢字。但一開始並沒有將碼錶全部填充使用完,只收錄了常用的6000多個漢字、英文及其它符號,這套標準稱為GB2312(資訊交換用漢字編碼字元集,GB是“國家標準”的簡化詞“國標”的拼音首字母縮寫,2312是國標序號)。後來又制定了一套收錄更多漢字的標準(收錄的漢字有2萬多個),稱為GBK(漢字編碼擴充規範,K是“擴”的拼音首字母)。
在GB2312或GBK裡,許多標點符號都使用2個位元組進行了重新編碼,這類佔2個位元組的標點符號稱為“全形”字元(“全形”也稱“全形”或“全寬”或“全碼”),原來ASCII碼錶中佔1個位元組的標點符號則稱為“半形”字元(“半形”也稱“半形”或“半寬”或“半碼”)。全形的逗號、括弧、句號等與半形是不一樣的:
在中文IME下,預設的標點符號是全形字元;在英文IME下,標點符號則是半形字元。
我們接著講故事:隨著使用電腦的國家越來越多,各個國家制定自己的電腦編碼通訊協定的情況也越來越多,導致的結果是:各國電腦的編碼互不支援、認識。比如在美國的電腦裡要顯示漢字,則必須安裝漢字系統才可以,否則中文檔案在美國系統的電腦中開啟便是亂碼。
就這樣,在這個時期催生出了一個叫ISO的國際組織(International Organization for Standardization,國際標準組織),著手解決各國的編碼問題。ISO統一製作了一個稱為UNICODE(統一碼、萬國碼、單一碼,Universal Multiple-Octet Coded Character Set,又簡稱為UCS)的編碼方案,用於收錄地球上所有文字和符號。UNICODE字元分為17組編排,每組編排稱為平面(Plane),每個平面擁有65536個碼位,共計可以收錄1114112個字元(111萬個字元,足夠大的容量)。UNICODE編碼統一一個字元佔2個位元組。
但UNICODE在很長一段時間內無法推廣,直到互連網的出現,資料的傳輸與交換使各國之間的編碼進行統一化成為迫切的需要。但早期的硬碟和網路流量都非常昂貴,UNICODE編碼裡的每個字元卻佔用了2個位元組的容量,於是為了節省檔案儲存體時所佔的硬碟空間,也為了節省字元在網路傳輸過程中所佔用的網路流量,又制定了基於UNICODE、面向傳輸的眾多標準,這些面向傳輸的標準統稱為UTF(UCS Transfer Format)。UNICODE編碼與UTF編碼並不是直接的一一對應,而是要通過一些演算法和規則來轉換。UNICODE與UTF的關係是:UNICODE是根本、基礎、目的,而UTF只是一種實現UNICODE的手段、方法、過程。
常見的UTF格式有:UTF-8,UTF-16,UTF-32。其中UTF-8是互連網上使用最廣的一種UNICODE的實現方式,它專為傳輸而設計。正因為UTF-8是基於UNICODE而設計的傳輸實現方式,所以它能使編碼無國界,任意國家的文字都能在任意國家的電腦瀏覽器中裡正常顯示。UTF-8最大的一個特點是:它是一種變長的編碼方式,它可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度,當能夠使用1位元組表示一個符號時,便使用1個位元組來表示,如果需要2位元組才能表示的符號,便使用2個位元組來表示,類推,直到4個位元組,從而節省硬碟儲存空間和網路流量。
所以我們的網站在開發時如果使用GB2312或GBK編碼,當別的國家的電腦不支援漢字編碼,那麼看到的將是亂碼,顯示出來類似這樣:口口口口口。而網站如果使用UTF-8編碼,則任意國家的電腦在開啟網站時其內容會自動轉換成UNICODE編碼,並且由於現在的電腦都支援UNICODE編碼,從而能正常顯示任意文字!
但是國內很多的網站仍然使用GB2312或GBK編碼,這類網站通常只面對國內使用者提供服務,面對國內使用者不會有顯示上的問題。只是如果面對其他國家的瀏覽者,這類網站被開啟時很大程度上將呈現亂碼。
為了網站的高相容性與國際化,推薦網站使用UTF-8編碼,而不是使用GB2312或GBK編碼。
指定網頁為UTF-8、GB2312和GBK的標籤分別為:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><meta http-equiv="Content-Type" content="text/html; charset=gbk">
那麼有一個問題出現了:網頁各種編碼的區別,僅僅是在於這一行meta標籤的設定差別嗎?僅僅是“utf-8”這5個字元換成“gb2312”這6個字元之類的這種“小差別”嗎?
不是的,差別不僅僅是這幾個字元的差別。當網頁指定meta標籤中的編碼為utf-8後,DreamWeaver在儲存網頁時會自動將網頁檔案儲存為utf-8的編碼格式(二進位碼使用utf-8的編碼格式),meta標籤中的utf-8編碼是為了告訴瀏覽器:這個網頁用的是utf-8編碼,請在顯示時使用utf-8編碼的格式解析並呈現出來;而如果meta標籤中指定編碼為gb2312,DreamWeaver在儲存網頁時會自動將網頁檔案儲存為gb2312的編碼格式(二進位碼使用gb2312的編碼格式),同樣,meta標籤中的gb2312編碼只是為了告訴瀏覽器:這個網頁用的是gb2312編碼,請在顯示時使用gb2312編碼的格式解析並呈現出來。我們做個實驗,將一個文字檔分別儲存為utf-8格式(開啟記事本建立文字檔,輸入內容後,選擇菜單:檔案→另存新檔,編碼選擇為UTF-8)和gb2312格式(另存時編碼選擇為ANSI,ANSI代表當前作業系統的預設編碼,在簡體中文Windows作業系統中,ANSI 編碼代表 GBK 編碼;在繁體中文Windows作業系統中,ANSI編碼代表Big5;在日文Windows作業系統中,ANSI 編碼代表 Shift_JIS 編碼,類推),對比其位元據。這裡使用UltraEdit-32檔案編輯器對文字檔進行16進位查看,即使用16進位查看檔案的位元據:
從中可以看到,使用utf-8編碼和使用gb2312編碼儲存的檔案,其位元據是不一樣的,即這兩個檔案的位元據內容是不一樣的。記事本軟體在開啟文字檔時,會嘗試識別檔案的編碼並進行解析和顯示,即文字儲存在記事本裡,無論儲存成utf-8編碼還是gb2312編碼,通常情況下記事本都能正常識別和顯示,不需要在檔案裡額外記錄資料以告知記事本該檔案是什麼編碼。但很多軟體卻無法做到智能識別文字檔的編碼,這就要求文字檔在儲存時,必須附帶一些特殊的內容(額外的資料)以告知該檔案是什麼編碼。UNICODE規範中有一個BOM(Byte Order Mark)的概念,就是位元組序標記,在檔案頭部開始位置寫入三個位元組(EF BB BF)以告知該檔案是utf-8編碼格式。但這個BOM又帶出了新的問題:不是所有的軟體或處理常式都支援BOM,即不是所有的軟體或處理常式都能識別檔案開頭的(EF BB BF)這三個位元組。當不支援識別時,這三個位元組又會被當成檔案的實際資料內容。早期的Firefox不支援對BOM的識別,當遇到BOM時會對這三個位元組顯示出特殊的亂碼符號;而到目前為止,PHP處理常式仍然不支援BOM,即當一個PHP檔案儲存為utf-8時,如果附帶了BOM,那麼PHP處理常式會將BOM解析為PHP檔案的實際資料內容而導致出錯!在DreamWeaver中,選擇軟體頭部菜單:修改→頁面屬性(也可以直接按快速鍵ctrl+j),在彈出的頁面屬性面板中點選“標題/編碼”,即可看到可供選擇的編碼。通常在改變網頁的編碼時,使用這種方式改變。如:
所以:當我們在meta標籤中設定為utf-8編碼格式時,網頁檔案就必須要儲存為utf-8格式,這樣瀏覽器才能正常顯示網頁而不是顯示亂碼。如果在meta標籤中設定utf-8編碼格式,網頁檔案卻儲存為gbk或其它格式,那麼在開啟網頁時瀏覽器會接到網頁meta標籤中格式的通知:使用utf-8編碼格式來解析和顯示網頁,而網頁的二進位碼(資料內容)卻為gbk編碼或其它格式,顯示出來就會是亂碼!這好比相親時,紅娘手裡的資料有誤,錯誤的告知男方:女方講英語(meta標籤中設定為utf-8編碼)。結果女方卻不懂英語(檔案卻不是utf-8編碼)。男方開口一句“Hello”就讓女方不知所謂了(亂碼)。
我們來實驗一下,網頁指定meta標籤中的編碼為utf-8,檔案卻儲存為gbk格式:我們先用DreamWeaver編輯一個utf-8格式的網頁並儲存,然後再用記事本開啟該網頁,另存新檔,編碼選擇為ANSI。
<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>中文</title></head><body>本檔案使用dreamweaver儲存後,再使用記事本開啟,並另存新檔ANSI編碼。</body></html>
在瀏覽器中的執行結果如下:
綜上所述:網頁開發時,盡量使用utf-8編碼格式,並且在儲存檔案時,儲存為utf-8編碼。(dreamweaver在儲存網頁檔案時,會根據<meta http-equiv="Content-Type" content="text/html; charset=編碼">所指定的編碼自動儲存為正確的對應編碼,但如果使用其它網站代碼編輯器,比如記事本、Editplus等,就需要注意,在儲存檔案時要選擇為正確的編碼)。