近日,一个同事问我,在windows下面用tomcat跑,不乱码,在linux下面用websphere跑乱码,是怎么回事,笼统的回答是windows和linux默认编码不一致造成,感觉没说清楚,没能说清楚,就代表自己还没掌握。好好梳理一下。
几个基本的知识点:
1、String其实和char数组等价,保存为2个字节,为unicode编码。存放在string里面的都是2个字节表示一个字符。
2、编码方式是针对子节流的,文档保存在磁盘,接收的网络数据都是以字节流方式存放。java读取文件,接收网络数据,应该先从new String(byte[],"编码方式")开始转换为String。
3、String(byte[],“编码方式”)意思为,将字节流以指定的格式解析,转换为unicode,存放在String内存中。
4、String的getBytes(“编码格式”)意思为将存放在String里面的字符流,按指定的编码格式转换为字节流。
5、页面提交的编码行为在http://www.ibm.com/developerworks/cn/java/book_global_development/6/有详细的说明。
服务器对提交参数解析行为,以tomcat5为例。
源码CoyoteRequest.java ---此为HttpRequest接口实现,就是servlet中调用的getParameter()
public String getParameter(String name) {
if (!requestParametersParsed)
parseRequestParameters();
return coyoteRequest.getParameters().getParameter(name);
}
protected void parseRequestParameters() {
.......
String enc = coyoteRequest.getCharacterEncoding(); -----获取浏览器提交的编码格式,也就是页面提交的Charset-Type指定的编码格式。
boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); ----tomcat
if (enc != null) {
parameters.setEncoding(enc);
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding(enc);
}
} else {
parameters.setEncoding
(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding
(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
}
}
//上面说明页面提交有指定编码格式,则按指定的编码格式解析,否则按默认的ISO-8859-1解析。
......
parameters.handleQueryParameters(); ---此方法调用processParameters
.....
}
public void processParameters( byte bytes[], int start, int len,
String enc ) {
..............
try {
addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
} catch (IOException e) {
// Exception during character decoding: skip parameter
}
..............
}
private String urlDecode(ByteChunk bc, String enc)
throws IOException {
if( urlDec==null ) {
urlDec=new UDecoder();
}
urlDec.convert(bc); ------urlDecode对每个byte进行还原。
String result = null;
if (enc != null) {
bc.setEncoding(enc);
result = bc.toString(); ------调用return new String( buff, start, end-start, enc );
} else {
。。。。
}
那我们看看不同服务器下面乱码的原因,做个小程序模拟一下不同服务器下面接收页面数据的流程
String a = "汉字"; ----假设页面为UTF-8编码,可以用System.out.println(System.getProperty(file.encoding)查看一下
try {
String b1 = URLEncoder.encode(a,"UTF-8"); //浏览器以UTF-8编码向服务器提交,进行一次编码
//将byte转换为8859-1
String d = URLDecoder.decode(b1,"ISO-8859-1"); //服务器先进行url解码,按默认的ISO-8859-1编码格式
String out = new String(d.getBytes("ISO-8859-1"),"UTF-8"); //服务器getParamet()进行一次编码转换,linux上面服务器的编码格式为UTF-8,用locale 可以查看一下
System.out.println("out----" + out);//这个等于在linux下面的输出
String out_gbk = new String(d.getBytes("ISO-8859-1"),"GBK"); //windows下面的tomcat默认编码格式为GBK
System.out.println("out_gbk----" + out_gbk);//此处表示tomcat输出为乱码
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
由此可以知道服务器编码格式和页面提交的编码格式不同,造成linux和windows下面web服务器显示一个正常,一个乱码。