在日常編寫代碼過程中,常常會(huì)碰到亂碼問題,一個(gè)典型的情況是瀏覽網(wǎng)頁,如果網(wǎng)站開發(fā)者缺少經(jīng)驗(yàn),就會(huì)帶來這種令人頭疼的問題。要了解亂碼的癥結(jié),我們就得從字符集和字符編碼說起,先來看看它們到底是什么:
1:字符集:是一個(gè)系統(tǒng)支持的所有抽象字符的集合。字符是各種文字和符號(hào)的總稱,包括各國家文字、標(biāo)點(diǎn)符號(hào)、圖形符號(hào)、數(shù)字等。
2:字符編碼:是一套法則,最常規(guī)的理解就是:讓程序根據(jù)這個(gè)法則對(duì)應(yīng)到相應(yīng)的字符集中將byte[]存取為string。
現(xiàn)在,我們要來看看這些東西在 .NET 中對(duì)應(yīng)的是什么。
一:字符集和字符編碼
如果想得到全部的字符集,則使用 System.Text.Encoding.GetEncodings() 方法,以下代碼用于列出.Net支持的全部字符集:
示例:
二:典型應(yīng)用場(chǎng)景之 HttpWebResponse
很多人都作過頁面抓取功能, HttpWebResponse 就會(huì)比較熟悉,當(dāng)然如果不嫌麻煩,也可以用 Socket 實(shí)現(xiàn),但是同時(shí)要解析很多屬性以及處理象重定向之類的諸多問題。
2.1 http header 和http content是什么?
瀏覽一個(gè)網(wǎng)頁,使用很多工具,或者使用.Net中的某些類進(jìn)行抓取,都給我們結(jié)構(gòu)化為 Http 頭和正文這樣的信息,其實(shí),當(dāng)我們發(fā)送一個(gè)請(qǐng)求,服務(wù)器返回給我們的是一串 byte[],我們完全可以自己去從這串 byte[] 解析出 http header 和 http content,它們之間其實(shí)僅僅非常簡單的以兩個(gè) /r/n/ 分割開而已,歷史上有著名的CRLF攻擊,CR就是/r,LF就是/n,就利用的是這個(gè)規(guī)則。
2.2 我們?nèi)绾尾炜磆ttp header,http content?
其實(shí)很簡單,既然這些都是 byte[] ,所以,我們只要知道這段 byte[] 正確的字符編碼規(guī)則,就能得到我們所需要看到的 html (html就是字符串而已)。使用 HttpWebResponse 這個(gè)類,就能請(qǐng)求一個(gè) url ,該類自動(dòng)為我們解析出了 httpheader ,有意思的是,它沒有給我們解析出 content ,所以,我們需要自己完成正文的byte[] to string。
2.3 http content to string的具體做法
好的,實(shí)際上,httpheader 中已經(jīng)告訴了我們一些字符集編碼相關(guān)的信息,我們可能感興趣,以及會(huì)混淆的這些http頭如下:
那么,這些 Http 頭在HttpWebResponse 中是怎么代表的呢?
一般會(huì)指定一個(gè)默認(rèn)的HttpWebResponse.Charaterset,默認(rèn)為"iso-8859-1"。
HttpWebResponse.ContentEncoding 代表的是 http頭中 Content-Encoding,與此類似的,還有一個(gè)http頭,為Transfer-Encoding。注意,很惡心的一點(diǎn)是
HttpResponse.ContentEncoding跟HttpWebResponse.ContentEncoding代表的不是一個(gè)東西,它和HttpResponse.Charaterset在MSDN上是一致的解釋。
之所以碰到以上問題,其實(shí)僅僅是因?yàn)椋?wù)器給我們傳回來的是byte[],而任何程序員在寫服務(wù)器端WEB程序的時(shí)候,都有可能有意或無意的轉(zhuǎn)碼出不規(guī)范的byte[]來。所以,如果我們嘗試從http頭的Content-Type和HttpWebResponse.Charaterset想要得到編碼規(guī)則,我們就敗了,我們敗在了有標(biāo)準(zhǔn),但是沒人嚴(yán)格去執(zhí)行標(biāo)準(zhǔn)。
有一些頗具迷惑性的API試圖在告訴我們,使用我你就能得到該流正確的Encoding了,比如,StreamReader.CurrentEncoding,我們可以把HttpWebResponse的GetResponse中讀取到
byte[],放置到MemoryStream中,然后利用如下代碼:
2.5 關(guān)于本例的一點(diǎn)補(bǔ)充
以上字節(jié)流的編碼解碼,很多地方用了Response做例子,但是,以上解碼針對(duì)的是非壓縮的Response,如果服務(wù)器已經(jīng)對(duì)http流進(jìn)行了壓縮(其壓縮格式在Content-Encoding中指明了),我們就得先解壓縮,再解碼Response流,然后再解碼正文。考慮到本文的主題,特意剪裁了對(duì)于 Response 流的解壓過程。
2.6 關(guān)于正確解碼的嘗試
有很多人嘗試從byte[]本身去解析和判斷編碼規(guī)則的API,如:codeproject上也有相關(guān)的文章,但是可悲的事實(shí)是:并沒有一種完美的方法來自動(dòng)判斷byte[]的編碼規(guī)則。還記得我們的瀏覽器(如IE)的編碼設(shè)置中的“自動(dòng)選擇”嗎,其實(shí)這個(gè)自動(dòng)選擇的錯(cuò)誤率還是蠻高的。所以,對(duì)于字節(jié)流的生成者,如BS程序開發(fā)者,可以通過規(guī)范輸出:聲明charset和編碼規(guī)范的方式,這樣才能讓解析者(如瀏覽器)解析的時(shí)候盡可能的少出現(xiàn)亂碼。
新聞熱點(diǎn)
疑難解答
圖片精選