通过前面中文化、国际化问题解决的系列1-4,相信大家对字符集、字符编码、字符解码、字符乱码、Java中文问题解决等都有了一个比较清晰的认识;但文中的信息并非包罗万象,结合到自己平时对于字符集、编码相关的一些疑惑,本篇对一些前文中并未提及的一些问题进行补充,以便让该系列更加完善和全面;本文主要解决以下两个问题:其一,解决UltraEdit菜单中的 文件 -> 转换 子菜单中涉及的一些名词疑惑,主要涉及EBCDIC 、OEM字符集、ANSI字符集、HZ编码等;其二,补充关于URL编码相关的一些知识点,涉及浏览器、Web服务器设置、Servlet规范等;
相信很多同学都有在用UltraEdit这个文本编辑器软件,从菜单 文件 --> 转换 进入即可看见很多的字符集间的转换子菜单,不同版本间可能会有些差异,可以参考下图:
从上图中我们可以看到很多前面已经提及的一些词汇,比如ASCII、Unicode [Big Endian/Little Endian]、UTF-8等;但还有一些未提及的,下面就逐个介绍下:
基于Java开发的Web应用URL组成如下:
http://domain:port/contextPath/servletPath/pathInfo?queryString
其中各个部分含义如下:
Domain、Port:分别是域名和端口;
contextPath:应用上下文路径,默认为应用名称,比如我们的apps;但可以通过应用服务器的相关配置进行修改,一般线上环境会修改成/,此时相当于contextPath为空;
servletPath:Servlet路径,一般在应用的web.xml文件中配置servlet-mapping;但由于现在的web应用一般都会用一些框架,比如Struts、Webwork等,此时各框架都会对此进行封装,会在另外的配置文件中进行设置;但原理都是一样的;
pathInfo:可以理解为最终接收用户请求的具体执行类,比如我们常说的Action;
queryString:get方式传入的请求参数;
以上各个部分中可能存在中文问题的是pathInfo、queryString两个部分;
首先,我们来看下Servlet中和URL相关的一些api及其注意事项:
HttpServletRequest.setCharacterEncoding(); //仅仅只适用于设置post提交的request body的编码而不是设置get方法提交的queryString的编码。该方法还告诉应用服务器应该采用什么编码解析post传过来的内容;注意:若没有设定characterEncoding,则使用ISO-8859-1来解码用户输入的表单,而不是使用系统默认的编码。
HttpServletResponse.setContentType(); //告诉浏览器网页中数据是什么编码;表单提交时,根据ContentType指定的charset对表单中的数据编码,然后发送给服务器。
HttpServletRequest.getParameter("name"); //返回的字符串为:queryString(包括get和post),其值经过Servlet服务器URL Decode过的,默认编码来源于应用服务器中的配置,比如tomcat中server.xml的URIEncoding。
HttpServletRequest.getPathInfo(); //返回的字符串为:pathinfo;由Servlet服务器解码(decode)过的。默认编码同上,tomcat中可设置useBodyEncodingForURI。
HttpServletRequest.getRequestURI(); //返回的字符串为:contextPath/servletPath/pathinfo;注意是浏览器提交过来的原始数据,未被Servlet服务器URL Decode过。
对URL编码【URL Encoding/Percent Encoding】时,使用以下规则:
字母数字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不变。
特殊字符 "."、"-"、"*" 和 "_" 保持不变。
空格字符 " " 转换为一个加号 "+"。
所有其他字符都是不安全的,因此首先使用一些编码机制将它们转换为一个或多个字节。然后每个字节用一个包含 3 个字符的字符串 "%xy" 表示,其中 xy 为该字节的两位十六进制表示形式。推荐的编码机制是 UTF-8。但是,出于兼容性考虑,如果未指定一种编码,则使用相应平台的默认编码。
假定我们待请求URL为:http://localhost:8080/example/中国?name=中国;
Html内content-type或meta中的charset=GBK;文件格式为ANSI/ASCII;
URL中的两个汉字"中国"的各字符集下的编码为:
汉字 | 编码 | 二进制表示 |
中国 | UTF-8 | 0xe4 0xb8 0xad 0xe5 0x9b 0xbd[-28, -72, -83, -27, -101, -67] |
中国 | GBK | 0xd6 0xd0 0xb9 0xfa[-42, -48, -71, -6] |
中国 | ISO8859-1 | 0x3f 0x3f[63, 63][??] |
对于Get方式的URL请求有两种情况,其一:用户直接在浏览器地址栏中输入URL,此时浏览器没有编码可参考,直接用浏览器的默认编码进行解析并提交到服务端;其二:在form表单内提交,只是form属性method为GET,此时浏览器会参考目前html中对编码的相关设置进行解析,比如content-type或meta中的charset。
以下就重点讲讲第二种方式的提交:
GET方式form submit:浏览器会对URL进行URL encoding,然后发送给服务器。
很显然,不同的浏览器以及同一浏览器的不同设置,会影响最终URL中PathInfo的编码,该编码可能不会由我们应用来控制;对于queryString,则是可以由我们的应用来完全控制的,对于上面的事例:中文的IE和FIREFOX都是采用GBK编码queryString。
若调整下上例中的假设条件,设置Html内content-type或meta中的charset=UTF-8;
此时在IE中queryString会按照UTF-8进行编码,即name=%E4%B8%AD%E5%9B%BD;
但是在非IE(Firefox、Chrome)中,此时提交时URL中会以中文直接提交,即name=中文;此时服务端的web服务器上肯定要进行相应的编码配置,否则肯定会出现乱码;
若设置Html内content-type或meta中的charset=ISO-5899-1;
此时在IE、Firefox、Chrome中queryString都被用ISO-5899-1编码了,即name= %26%2320013%3B%26%2322269%3B;
对于编码串中的%26、%3B应该是百分号编码【Percent Encoding】中的保留字符,分别对应&、;,两者之间是经过编码的十进制码;对于这点偶也不是十分肯定?要是有同学比较清楚,请告诉偶下,thx。
POST方式提交:表单中的参数值对是通过request body发送给服务器,此时浏览器会根据网页的ContentType("text/html; charset=GBK")中指定的编码进行对表单中的数据进行编码,然后发给服务器。
在服务器端的程序中我们可以通过Request.setCharacterEncoding() 设置编码,然后通过request.getParameter获得正确的数据。
小结:
相关文档参考:
字符,字节和编码:http://www.regexlab.com/zh/encoding.htm
各种字符集和编码详解:http://blog.csdn.net/ancky/archive/2008/01/11/2034809.aspx
深入浅出URL编码:http://blog.csdn.net/yzhz/archive/2007/07/03/1676796.aspx
javascript html 相关编码问题研究:http://stauren.net/log/fpev3c89q.html
J2EE Web组件中中文及相关的问题(系列):http://blog.csdn.net/whodsow/archive/2003/10/27/19465.aspx