服务器返回乱码页面,请求的数据发送到服务器后取出来是乱码,以上两个问题是web开发人员经常遇到的问题,解决这类问题需要理解乱码问题的根源所在。
1. 字符编码
字符是以二进制编码的形式保存在存储器中的,如:“我”这个字,可以用gbk的方式保存(用字节表示是[-50, -46]),也可以用utf-8的方式保存(用字节表示是[-26, -120, -111])。程序在读取数据块时需要一个字节一个字节的读取,然后将字节转换为字符,显然如果程序不知道字节是表示的什么编码的字符,读出来就会出问题,这就如同你说了一句话,我如果事先不知道你说的将是什么语言,就没法去翻译了,如果你说的英语,我以为是日语,然后按日语的方式来理解到我的主观意识,显然结果就是不知你在说什么了,就成了我们程序中的乱码。所以对于前面我所表示的字节数组byte[] data = {-26, -120, -111},我们必须这样做才能得到正确的字符: String s = new String(data, "utf-8"),(注:这里用了字符串,因为字符串就是由一个个字符组成的),如果我们不指定参数里面的"utf-8",那么系统就会用操作系统默认的编码了,这可能是gbk或是什么任何编码。
2. jsp服务器返回乱码页面
2.1 pageEncoding
在jsp页面的page指令中我们指定了pageEncoding属性,这个属性就是告诉jsp容器如何读取这个jsp页面,所以这个属性必须与jsp页面保存的编码保持一致。也就是说,如果你页面的编码保存为gbk, 而pageEncoding设置成了utf-8,则jsp容器在读这个jsp页面的时候就会出错(如果存在非英文字符的话)。jsp容器读jsp的目的是将其翻译成java代码,所以如果读错了jsp页面,翻译出来的java代码也就会出错,如果这种错误影响了java文件的语法,就会在访问时出现无法编译jsp的语法错误,如果没有影响到语法,就会出现最终显示的html页面上有乱码的错误。所以如果遇到显示乱码,则检查pageEncoding是否正确。
2.2 contentType
page指令中的contentType属性用于指定返回给浏览器的数据的文档类型,服务器通过http头信息返回给浏览器这个信息,所以在浏览器 html代码中用户是看不到的。同时contentType属性还可以指定页面的编码,即服务器即以什么编码发送页面数据。比如说中文数据,可以用gbk 或utf-8的方式来发送,这个编码跟jsp页面的编码没有关系,只要设定的编码支持页面中的字符就行了。相同于有了一个字符串s="中国人",然后用 s.getBytes("gbk")的方式来发送s。所以由于contentType错误出现乱码的概念不高,但也要注意一下,比如说如果设置成了"iso8859-1",则浏览器就会显示乱码了。contentType还有一个用,就是浏览器将会依据这个编码来显示页面,在IE下点右键,然后选择“编码”,你就可以注意到页面是以什么编码显示的了。
2.3 如果是servlet返回的结果
上面说的是jsp,如果servlet的话就要注意设置response.setCharacterEncoding(""),如果没有设置,服务器会默认为是iso8859-1,设置后得到的writer(即response.getWriter())对象,就会依据这个编码来向客户端写数据,writer对象的构造与以下方式类似:PrintWriter pw = new PrinterWriter(new OutputStreamWriter(socket.getOutputStream(), "编码")),这里提到了通过socket得到输出流,不明白的话可以参考我的另一篇文章。pw.write("你好"),实际上就是先通过byte[] data = "你好".getBytes("编码"),然后将data写给客户端。
)
3. 服务器得到客户端传过来的数据为乱码
3.1 通用解决方案
String param = request.getParameter("paramName"),如果浏览器传过来的为中文,则取出来的数据将是乱码。为什么呢?因为客户端只能将数据的编码传给服务器,如[-26, -120, -111],但服务器并不知道这是什么字符集的编码,于是假定为iso8859-1, 用这种方式构造了字符串s = new String(data, "iso8859-1"),显然这样肯定是乱码。解决方案很简单,我们得到值param后,用byte data[] = param.getBytes("iso8859-1"),这样data就是客户端传过来的真实编码,然后我们再重新创建字符串:param = new String(data, "正确的编码");
3.2 POST请求
如于post请求处理起来更简单,get请求与post请求向服务器发送数据的方式不一样,get请求的参数是通过HTTP头信息中的第一行数据发送的,是URI的一部分,而post请求则是在发送完HTTP头信息后作为单独的数据块发送的。因此对于get请求的参数,我们在使用request之前服务器已经读出来了,已经是乱码了,只能用前面的方案,但对于post请求的数据,我们在调用getParameter或getReader之前,服务器并没有去处理,所以我们可以在getParameter之前先告诉服务器正确的编码,通过request.setCharacterEncoding("正确的编码"),然后再读取参数。
1. 字符编码
字符是以二进制编码的形式保存在存储器中的,如:“我”这个字,可以用gbk的方式保存(用字节表示是[-50, -46]),也可以用utf-8的方式保存(用字节表示是[-26, -120, -111])。程序在读取数据块时需要一个字节一个字节的读取,然后将字节转换为字符,显然如果程序不知道字节是表示的什么编码的字符,读出来就会出问题,这就如同你说了一句话,我如果事先不知道你说的将是什么语言,就没法去翻译了,如果你说的英语,我以为是日语,然后按日语的方式来理解到我的主观意识,显然结果就是不知你在说什么了,就成了我们程序中的乱码。所以对于前面我所表示的字节数组byte[] data = {-26, -120, -111},我们必须这样做才能得到正确的字符: String s = new String(data, "utf-8"),(注:这里用了字符串,因为字符串就是由一个个字符组成的),如果我们不指定参数里面的"utf-8",那么系统就会用操作系统默认的编码了,这可能是gbk或是什么任何编码。
2. jsp服务器返回乱码页面
2.1 pageEncoding
在jsp页面的page指令中我们指定了pageEncoding属性,这个属性就是告诉jsp容器如何读取这个jsp页面,所以这个属性必须与jsp页面保存的编码保持一致。也就是说,如果你页面的编码保存为gbk, 而pageEncoding设置成了utf-8,则jsp容器在读这个jsp页面的时候就会出错(如果存在非英文字符的话)。jsp容器读jsp的目的是将其翻译成java代码,所以如果读错了jsp页面,翻译出来的java代码也就会出错,如果这种错误影响了java文件的语法,就会在访问时出现无法编译jsp的语法错误,如果没有影响到语法,就会出现最终显示的html页面上有乱码的错误。所以如果遇到显示乱码,则检查pageEncoding是否正确。
2.2 contentType
page指令中的contentType属性用于指定返回给浏览器的数据的文档类型,服务器通过http头信息返回给浏览器这个信息,所以在浏览器 html代码中用户是看不到的。同时contentType属性还可以指定页面的编码,即服务器即以什么编码发送页面数据。比如说中文数据,可以用gbk 或utf-8的方式来发送,这个编码跟jsp页面的编码没有关系,只要设定的编码支持页面中的字符就行了。相同于有了一个字符串s="中国人",然后用 s.getBytes("gbk")的方式来发送s。所以由于contentType错误出现乱码的概念不高,但也要注意一下,比如说如果设置成了"iso8859-1",则浏览器就会显示乱码了。contentType还有一个用,就是浏览器将会依据这个编码来显示页面,在IE下点右键,然后选择“编码”,你就可以注意到页面是以什么编码显示的了。
2.3 如果是servlet返回的结果
上面说的是jsp,如果servlet的话就要注意设置response.setCharacterEncoding(""),如果没有设置,服务器会默认为是iso8859-1,设置后得到的writer(即response.getWriter())对象,就会依据这个编码来向客户端写数据,writer对象的构造与以下方式类似:PrintWriter pw = new PrinterWriter(new OutputStreamWriter(socket.getOutputStream(), "编码")),这里提到了通过socket得到输出流,不明白的话可以参考我的另一篇文章。pw.write("你好"),实际上就是先通过byte[] data = "你好".getBytes("编码"),然后将data写给客户端。
)
3. 服务器得到客户端传过来的数据为乱码
3.1 通用解决方案
String param = request.getParameter("paramName"),如果浏览器传过来的为中文,则取出来的数据将是乱码。为什么呢?因为客户端只能将数据的编码传给服务器,如[-26, -120, -111],但服务器并不知道这是什么字符集的编码,于是假定为iso8859-1, 用这种方式构造了字符串s = new String(data, "iso8859-1"),显然这样肯定是乱码。解决方案很简单,我们得到值param后,用byte data[] = param.getBytes("iso8859-1"),这样data就是客户端传过来的真实编码,然后我们再重新创建字符串:param = new String(data, "正确的编码");
3.2 POST请求
如于post请求处理起来更简单,get请求与post请求向服务器发送数据的方式不一样,get请求的参数是通过HTTP头信息中的第一行数据发送的,是URI的一部分,而post请求则是在发送完HTTP头信息后作为单独的数据块发送的。因此对于get请求的参数,我们在使用request之前服务器已经读出来了,已经是乱码了,只能用前面的方案,但对于post请求的数据,我们在调用getParameter或getReader之前,服务器并没有去处理,所以我们可以在getParameter之前先告诉服务器正确的编码,通过request.setCharacterEncoding("正确的编码"),然后再读取参数。