ASP.NET乱码深度剖析
写在前面
在Web开发中,乱码应该算一个常客了。今天还好好的一个页面,第二天过来打开一看,中文字符全变“外星文”了。有时为了解决这样的问题,需要花上很长的时间去调试,直至抓狂,笔者也曾经历过这样的时期。有时虽然是“侥幸”解决了,但对其中的原理却一知半解。
为了弄清楚这个问题,今天查了大半天的资料、测试。现把这些点滴记录下来,以激励自己重视基础,同时和大家分享一下,望大家不吝批评指正。
预备知识
先介绍一些字符编码方面的基本知识,如果你对这些已经比较了解了,请直接跳过此节。
1. 字符集与字符编码概述
简单来说,字符集就是与特定区域相关的一系列有效字符的有序集合,比如字母、数字、标点符号等。注意关键字“有序”,表明集合中的每一个字符都是具有唯一数字编号(码值)的。不同国家使用的语言文字、符号不一样,相应的字符集必定也不一样。比如中国使用汉字,美国使用英语,韩国使用韩文,等等。
字符集是为了信息交互而设计的,最终还是要转化成计算机的表示法。我们知道,计算机只认识0和1,它对字符集符号不感冒。所以,我们必须想办法把字符转化为0和1的序列。我们知道,计算机最小的存储单位是位(bit),程序中一般使用的最小单位是字节(byte)。为了把字符存储到计算机中,我们就要考虑用几个byte几个bit,考虑每一个bit上是0还是1,考虑存储和读取效率,并且必须兼顾整个字符集,这就是字符编码。
一句话,字符集只关心字符的定义,而字符编码负责字符的存储和读取细节。用三层模式来打比喻的话,字符集是模型层,而字符编码是业务层。注意:一般常说的GB2312、GBK等其实同时包含了这两方面的定义
2. 常用中文字符编码简介
GB2312
GB2312的全称是《信息交换用汉字编码字符集-基本集》,由国家标准总局于1980发布,1981年5月1日施行,中国大陆、新加坡使用此编码。基本集收录了6763个汉字,只能显示简体汉字。
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服务器是美国人,他们两个的“编码”(语言)不一致,悲催的结局不言而喻。
ASP.NET中请求响应编码的设置
你可以在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代码
补充:Web开发 , ASP.Net ,