在Web開發中,亂碼應該算一個常客了。今天還好好的一個頁面,第二天過來打開一看,中文字符全變“外星文”了。有時為了解決這樣的問題,需要花上很長的時間去調試,直至抓狂,筆者也曾經歷過這樣的時期。有時雖然是“僥幸”解決了,但對其中的原理卻一知半解。
為了弄清楚這個問題,今天查了大半天的資料、測試?,F把這些點滴記錄下來,以激勵自己重視基礎,同時和大家分享一下,望大家不吝批評指正。
先介紹一些字符編碼方面的基本知識,如果你對這些已經比較了解了,請直接跳過此節。
1. 字符集與字符編碼概述
簡單來說,字符集就是與特定區域相關的一系列有效字符的有序集合,比如字母、數字、標點符號等。注意關鍵字“有序”,表明集合中的每一個字符都是具有唯一數字編號(碼值)的。不同國家使用的語言文字、符號不一樣,相應的字符集必定也不一樣。比如中國使用漢字,美國使用英語,韓國使用韓文,等等。
字符集是為了信息交互而設計的,最終還是要轉化成計算機的表示法。我們知道,計算機只認識0和1,它對字符集符號不感冒。所以,我們必須想辦法把字符轉化為0和1的序列。我們知道,計算機最小的存儲單位是位(bit),程序中一般使用的最小單位是字節(byte)。為了把字符存儲到計算機中,我們就要考慮用幾個byte幾個bit,考慮每一個bit上是0還是1,考慮存儲和讀取效率,并且必須兼顧整個字符集,這就是字符編碼。
一句話,字符集只關心字符的定義,而字符編碼負責字符的存儲和讀取細節。用三層模式來打比喻的話,字符集是模型層,而字符編碼是業務層。注意:一般常說的GB2312、GBK等其實同時包含了這兩方面的定義
2. 常用中文字符編碼簡介
GB2312
GB2312的全稱是《信息交換用漢字編碼字符集-基本集》,由國家標準總局于1980發布,1981年5月1日施行,中國大陸、新加坡使用此編碼?;炯珍浟?763個漢字,只能顯示簡體漢字。
GBK
1995頒布,全稱是《漢字編碼擴展規范》。在GB2312的其他上,增加了繁體漢字,支持ISO/IEC 10646-1 和GB-13000-1的全部中、日、韓(CJK)字符,共20902個。向下兼容GB2312。
GB18030
全稱是《信息交換用漢字編碼字符集基本集的擴充》,目前兩個版本,分別于2000年和20005年頒布。該字符集收錄了70000多個漢字,包括了藏、蒙古、維吾文等少數民族字符,是我國計算機系統必須遵循的基礎性標準之一。向下兼容GBK和GB2312。
BIG5
臺灣和港臺地區使用的漢字編碼,俗稱“大五碼”,共收錄了13060個漢字。
UTF-8
這是目前使用最多的一種Unicode編碼,是Visual Studio內置的編碼,相信大家一定都不陌生。根據字符碼值的不同,可能用1、2、3個字節表示。
注意,編碼之間一般都不是兼容的。其它編碼在此不作介紹,若想進一步了解字符編碼,請看我收藏的一篇文章:http://blog.csdn.net/tomysea/article/details/6712344
3. 字符串、字符數組和字節數組
C#中的字符串(string)和字符(char)其實都是對象,他們有相應的類String和Char,string和char只是這兩個類的一別名而已,內部都是采用Unicode碼值表示。請注意我說的是碼值,不是編碼。
我們已經知道,Unicode的字符大多是多字節表示的,那么一個char就得用幾個byte來表示。這里我要說的重點是,使用不同的編碼表示字符串,其對應的byte可能是不一樣的。請看下面的代碼,注意輸出字節數部分。UTF-8編碼的字節數是22,而GB2312編碼的字節數是16。
string title = "2012真的來了嗎?"; //字符串
char[] chars = title.ToCharArray(); //字符數組
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(title);
Response.Write(chars.Length + " "); //10 (字符數)
Response.Write(bytes.Length + " "); //22 (UTF-8編碼的字節數)
bytes = System.Text.Encoding.GetEncoding("GB2312").GetBytes(title);
Response.Write(bytes.Length + " "); //16 (GB2312編碼的字節數)
從http請求響應模型說起
http是一個請求/響應的模型,這個我們大家都知道。http請求可以分為請求頭和請求實體兩部分,相應地http響應也可以分為響應頭和響應實體。請求頭或響應頭是瀏覽器與Web服務器通信用的(假定用瀏覽器訪問Web服務器),而實體則是實際發送的數據,比如Form表單的數據、Ajax提交的數據、傳回來的html代碼等。不管是瀏覽器還是Web服務器,在發送實體前都會把它轉換為字節流。明白這一點很重要,因為涉及字節流就一定會與字符編碼有關。
從上面的請求響應模型中我們可以得出一個結論:請求和響應編碼必須嚴格保持一致!為什么呢?這很好理解,瀏覽器和Web服務器是要通信的,如果編碼不一樣的話,勢必會造成許多“誤解”。假設瀏覽器是中國人(不懂E文),而Web服務器是美國人,他們兩個的“編碼”(語言)不一致,悲催的結局不言而喻。
你可以在machine.config或web.config文件指定全局配置,也可以在頁面級特別指定。如果你未手動指定且machine.config中也為空,則默認會讀取計算機上“區域選項”中的設置。
1. 全局配置
在machine.config或web.config文件(根目錄或者子目錄都有效)中的system.web節點中配置globalization節點。如果在根目錄下的web.config配置,則會響應整個網站,若只是在子目錄下配置,則只會響應該目錄及其子目錄。 詳細配置如下:
<system.web>
<globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8"/>
<!--按順序是:文件編碼 請求編碼 響應編碼-->
<!—-fileEncoding會在后面說到-->
<!--后面還有其它配置-->
2. 頁面級的配置
在aspx頁面的Page指令中設置響應編碼
<%@ Page Language="C#" AutoEventWireup="true" ResponseEncoding="utf-8"
CodeBehind="byte.aspx.cs" Inherits="DevKit.Web.test.charset._byte" %>
在aspx頁面中手動指定meta標簽
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
在后臺cs文件中配置
Request.ContentEncoding = System.Text.Encoding.UTF8; //請求編碼
Response.ContentEncoding = System.Text.Encoding.UTF8; //響應編碼 接下來,我們從幾個示例中去體驗亂碼,從而總結出解決亂碼的一般方法。
操作系統:Windows xp PRofessional SP3 雨林木風版
開發環境:Visual Studio 2008 專業版 + SP1(.NET 3.5)
Web容器:VS集成的Development Server
瀏覽器:IE8 、Firefox 5
實例1 aspx頁面提示意外的字符“XXX”,引號里面是亂碼
背景
網站配置了在根目錄配置了文件、請求、響應編碼都為utf-8,頁面成功編譯,沒有任務錯誤。詳細錯誤見下圖:
html代碼
<div>
aspx頁面中的中文
<br />后臺的中文變量:<em><%=汽車%></em>
</div>
后臺代碼
view plain
public partial class _byte : System.Web.UI.Page
{
protected string 汽車= "我是凱迪拉克"; //別懷疑,中文變量是可以的:)
protected void Page_Load(object sender, EventArgs e)
{
//...
}
}
分析與解決
既然web.config已經配置了一樣的請求響應編碼,而且頁面級別也沒設置,可以排除這方面的問題了。注意到文件編碼是UTF-8,會不是會文件編碼引起的呢?(提示:這里的文件編碼指的是保存文件時指定的編碼,點擊“另存為…”,在彈出的窗口中選擇“編碼保存”可以看到)。果然,此aspx頁面的保存編碼為GB2312,與web.config文件不一樣,把它修改為UTF-8。
小提示:UTF-8有兩種編碼:UTF-8(帶簽名)和UTF-8(無簽名)。帶簽名的UTF-8會在文件的開頭寫入“EF BB BF”(16進制),以標示自己采用的編碼格式,這個標志稱為BOM(Byte Order Mark),即字節序。打個比方,UTF-8(帶簽名)戴了?;盏膶W生,就算不認識他的人,一看?;站兔靼琢?;而UTF-8(無簽名)則是沒戴?;盏?。這里的?;站褪俏覀冋f的BOM,一個能夠表明自己身份的標志。
小結
這是因為文件的保存編碼與當前網站指定的文件編碼不一致引起的,所以最佳實踐是:手動在web.config中指定文件編碼,并確保所有頁面的保存編碼與web.config一致。
其實最容易出這種問題的是js和CSS文件,如果你用其它工具(比如Dreamweaver)來編寫這些文件卻采用不同的編碼保存,一旦文件包含中文就可能出這樣的錯誤,導致js腳本錯誤,css無效!
實例2 跨頁post提交時接收的Form數據變成了亂碼
背景
有兩個頁面,注冊頁面(register.html)和處理注冊的頁面(handle.aspx),注冊頁面的表單信息以post方式提交到handle.aspx。根目錄的配置的文件編碼、請求編碼和響應編碼都是UTF-8。
register.html頁面的關鍵html
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312" />
</head>
<body>
<form id="form1" name="form1" action="handle.aspx" method="post">
<input type="text" id="txtName" name="txtName" />
新聞熱點
疑難解答