做web應用程序開(kāi)發(fā)也有很多年了,前后涉及有,asp,asp.net,jsp,php 。盡管語(yǔ)言都不相同,在日常開(kāi)發(fā)中,無(wú)論那門(mén)語(yǔ)言都出現過(guò),瀏覽器端的亂碼問(wèn)題。 出現了,都會(huì )手忙腳亂一陣,上網(wǎng)查資料,一頁(yè)一頁(yè)看。 還有些緊張,因為boss可能還等著(zhù)解決問(wèn)題呢。 想必這些情況,做web開(kāi)發(fā)的同人也是經(jīng)常遇到的。 下面要講的是比較原理性的,我想如果對于亂碼產(chǎn)生原因找到了,以后出現類(lèi)似問(wèn)題。按照原理推論,一定也會(huì )很快解決。 (以下說(shuō)明文件,都是以文本文件說(shuō)明)
一、文本文件編碼是什么?
A、文本文件編碼存在哪里了呢?
我們知道,計算機存儲文件,最終都是以二進(jìn)制保存的,通過(guò)流方式讀取任何文件,得到是8位的字節流。一個(gè)文件生成了,同時(shí)也確定了它的編碼了。 以下通過(guò)軟件:winhex比較說(shuō)明:
通過(guò)使用記事本:輸入“中”,另存為時(shí)候,分別選擇:utf-8,unicode,ansi編碼。我們通過(guò)winhex打開(kāi)比較下。
utf-8:EF BB BF E4 B8 AD 6字節
unicode:FF FE 2D 4E 4字節
ansi(gb2312):D6 D0 2字節
同一個(gè)漢字中,保存為文件后,實(shí)質(zhì)存儲的字節碼各不相同。
這里我們一定想到一個(gè)問(wèn)題,讀取該文件的軟件打開(kāi)這個(gè)文件,怎么都可以顯示出:“中”呢?
如果我們繼續思考下就會(huì )想到: 這個(gè)文本文件的編碼是不是存在文件屬性中了呢?
答案是:查看屬性沒(méi)有任何不同
那文件編碼存在:文件的字節碼里面了?
哈哈,我也是這么想的,通過(guò)查閱資料知道,unicode碼可以指定一個(gè)bom(順序頭),unicode常見(jiàn)表現形式有:utf-8,utf-16 順序頭不一樣,因此很多軟件可以通過(guò)這個(gè)標記來(lái)區分文件是什么編碼了。 上面例子,utf-8頭是:EFBBBF,unicode LE是:FFFE.
是不是所有文件編碼都會(huì )寫(xiě)入到文件字節碼里面呢?
如果真的這樣的話(huà),那么問(wèn)題就簡(jiǎn)單了。所有應用程序讀文本文件時(shí)候,讀一下標記,那么就知道它是什么編碼。 其實(shí),除了unicode有bom這個(gè)特殊頭外,其它編碼可沒(méi)有呢。 看到剛才那個(gè):ansi(gb2312)編碼了嗎,值是:D6D0 剛才是“中” 在gb2312編碼表中的代號。
B、怎么樣獲得文件編碼呢?
上個(gè)例子中,我們知道,文本文件編碼不可能都保存在文件字節碼中。 那么,應用程序讀取文件怎么樣判斷編碼呢?
我們用:zend 工具,以EUC-JP生成一個(gè)文件,內容是:”中” ,通過(guò):winHEX查看該文件,它的字節碼是:C3 E6 ??梢钥吹酵瑯佣际牵?中” gb2312保存文件,得到字節碼是:D6 D0 .
我用windows 記事本打開(kāi):2個(gè)文件看一下:
怎么我文件內容是:”中“,用euc_jp編碼保存后,用記事本打開(kāi),看到是:”面“ 了呢? 是不是windows 記事本有問(wèn)題呢?
記事本沒(méi)有任何問(wèn)題,windows 記事本在打開(kāi)文本文件時(shí)候,會(huì )先判斷bom類(lèi)標記,如果發(fā)現文本文件存在該標記。那么,就能夠知道它對應的編碼了。然后,將字節流轉換為對應編碼字符串。 這樣顯示正常保存時(shí)候內容。
如果,沒(méi)有bom頭標記的文本,記事本程序,就默認當:ANSI編碼處理了(在簡(jiǎn)體中文系統下,ANSI編碼代表GB2312編碼,在日文操作系統下,ANSI編碼代表JIS編碼),在簡(jiǎn)體中文下,ANSI對應是:gb2312。因此,記事本把”C3E6”當作gb2312處理。 C3E6在gb2312編碼表中對應是漢字:“面” 了。
文本文件,保存時(shí)候存儲編碼,與讀出時(shí)候設置編碼,如果不統一,就會(huì )造成亂碼!
二、php引擎怎么樣獲得我的文件編碼
其實(shí),這里說(shuō)php引擎處理編碼,與記事本識別編碼基本是相同的。發(fā)現能夠識別編碼就識別,不能識別編碼就按照:
php.in 中
default_charset = "iso-8859-1"
default_charset 指定編碼處理了,默認是:iso-8859-1。
這里啰嗦一下,現在很多應用程序都會(huì )用:"iso-8859-1" 作為默認編碼,jsp也是這樣的。它的優(yōu)點(diǎn)大家應該可以理解:iso-8859-1(EASCII 擴展ASCII編碼)以ASCII為基礎,在空置的0xA0-0xFF的范圍內,加入96個(gè)字母及符號,藉以供使用附加符號的拉丁字母語(yǔ)言使用。用一個(gè)8位字符可以表示任意字符集。在程序開(kāi)發(fā)中,我們常用的變量,都會(huì )是英文字符空格之類(lèi),這些都會(huì )在解析時(shí)候保持原來(lái)不變。而對于的中文字符,都會(huì )是多字節的。iso-8859-1解析后,會(huì )變成一些西歐字符。 但是實(shí)際上,并不會(huì )影響程序任何邏輯。 因為那些亂七八糟的西歐字符,只是一些注釋?zhuān)蛘咧刀选?程序語(yǔ)法完全沒(méi)有破壞??!
處理完的結果它還是字節流,準備發(fā)送給瀏覽器。
三、瀏覽器怎么樣獲得服務(wù)端的編碼
這里我們知道,瀏覽器得到web服務(wù)端返回的文件流,默認是:iso-8859-1 字節碼。其實(shí)可以看作是與原始存文件流的字節碼是相同的。
瀏覽器也是一個(gè)軟件,它獲得了字節流,它從那里知道字節流是什么編碼呢?
這里有幾種形式:
A、HTTP resposne頭告知編碼
以下是:訪(fǎng)問(wèn):baidu.com httpwatch 抓包圖:
我們只抓取了:Stream這個(gè)選項卡,可以通過(guò)這里看到,右下返回內容是亂碼,其實(shí)這些就是服務(wù)器返回字節流。 再往上看:
這里有個(gè):charset。 哈哈,它就是服務(wù)端返回給瀏覽器的一個(gè)編碼。瀏覽器通過(guò)這個(gè)編碼就能夠知道用什么編碼類(lèi)解析該字節流了。 注意,返回charset編碼,一定要與該文件流保存編碼一致。否則一樣亂碼!B、通過(guò)html源碼,meta頭告知
并不是所有服務(wù)器端程序都會(huì ),都是很么按章出牌,告知自己流編碼。很多時(shí)候,php開(kāi)發(fā)人員沒(méi)有加入:
header("Content-type:text/xml;charset=字符編碼"); 那么瀏覽器還有其它方法處理嗎?
如果meta頭指定了,瀏覽器也一樣知道編碼是什么了。
C、有一些問(wèn)題:
如果header,meta兩個(gè)個(gè)都指定了以那個(gè)為準呢?
測試表明,以http 協(xié)議中,response 頭里面的charset為主。 會(huì )忽視掉,meta指定。
如果header,meta兩個(gè)都沒(méi)有指定,會(huì )出現什么情況呢?
這個(gè)時(shí)候,就像上面例子里面的記事本了。 會(huì )怎么出現依據瀏覽器自己本身設置默認編碼。如果你文件剛好是:gb2312編寫(xiě),查看瀏覽器默認編碼剛好也是:gb2312,那么你的中文字符顯示正常。如果換上一個(gè)默認字符不一樣,就會(huì )出現亂碼情況。 這種情況,估計做web開(kāi)發(fā)同人經(jīng)常會(huì )遇到吧!
這篇文章,只是我charset字符集中一部分。比較基礎,以后我會(huì )寫(xiě)寫(xiě)其它字符集相關(guān)的。歡迎朋友點(diǎn)評!
聯(lián)系客服