Java乱码问题解决方法_动力节点Java学院整理
1.文件页面编码导致的乱码。
每一个文件(java,js,jsp,html等)都有其本身的编码格式,文件中的代码在一种编码中显示正常,在另外一种编码下就会显示出乱码。
在eclipse中,每一个工程都会有编码格式(text file encoding), 一般默认为gbk。而一个比较好的编程习惯是新建一个项目,优先把项目的编码设为utf-8。
这样做的原因很简单,utf-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。几种常见的字符集,gbk,gb2312,utf-8之间的关系如下:
gbk是国家标准gb2312基础上扩容后兼容gb2312的标准。gbk、gb2312等与utf8之间都必须通过unicode编码才能相互转换
2.不同字符集的字符串转换时导致的乱码。
每一个string,底层实现都是用一个byte数组存储,使用不同的字符集,存储的数组长度当然就不同。如果不使用同一种字符集进行解码,就一定会出现乱码。
例如如下代码:
java代码
import java.io.unsupportedencodingexception; import java.nio.charset.charset; public class testcharset { public static void main(string[] args) throws unsupportedencodingexception { string strchinesestring = "中文"; string encoding = system.getproperty("file.encoding"); system.out.println("系统默认的字符集是:" + encoding); system.out.println(strchinesestring.getbytes(charset.forname("gbk")).length); system.out.println(strchinesestring.getbytes(charset.forname("utf-8")).length); system.out.println(strchinesestring.getbytes().length); } }
输出结果为:
java代码
1.系统默认的字符集是:utf-8
2.4
3.6
4.6
可以看出,使用gbk和utf-8编码,得到的byte数组长度不一样,原因就是utf-8使用3个字节来编码中文,而gbk使用2个字节来编码中文。因为我的项目默认使用utf-8,所以使用不加参数的getbytes()得到的数组长度和使用utf-8编码的 字符串长度一样。关于字符集的详细知识可以参考第一部分中给出的文章地址。
jdk中关于getbytes方法的描述:
getbytes() 使用平台的默认字符集将此 string 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
getbytes(charset charset) 使用给定的 charset 将此 string 编码到 byte 序列,并将结果存储到新的 byte 数组。
每一个字符串底层都有自己的编码方式。不过一旦调用getbyte方法后,得到的byte数组就是使用某种特定字符集编码后的数组,不需要再做多余的转换。
当得到上面的byte数组后,就可以调用string的另外一个方法来生成需要转码的string了。
测试例子如下:
java代码
import java.io.unsupportedencodingexception; import java.nio.charset.charset; public class testcharset { public static void main(string[] args) throws unsupportedencodingexception { string strchinesestring = "中文"; byte[] bytegbk = null; byte[] byteutf8 = null; bytegbk = strchinesestring.getbytes(charset.forname("gbk")); byteutf8 = strchinesestring.getbytes(charset.forname("utf-8")); system.out.println(new string(bytegbk,"gbk")); system.out.println(new string(bytegbk,"utf-8")); system.out.println("**************************"); system.out.println(new string(byteutf8,"utf-8")); system.out.println(new string(byteutf8,"gbk")); } }
输出结果为:
java代码
1.中文
2.����
3.**************************
4.中文
5.涓枃
可以看出,使用哪种字符集编码一个string,在生成一个string的时候就必须使用相应的编码,否则就会出现乱码。
简单来讲,只有满足如下公式的string转码,才不会乱码。
java代码
string strsource = "你想要转码的字符串"; string strsomeencoding = "utf-8"; //例如utf-8 string strtarget = new string (strsource.getbytes(charset.forname(strsomeencoding)), strsomeencoding);
jdk中关于getbytes方法的描述:
string(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 string。
string(byte[] bytes, charset charset) 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 string。
3.socket网络传输时导致的中文乱码。
使用socket进行通讯的时候,传输有多种选择,可以使用printstream,也可以使用printwriter。传输英文还好,传输中文就可能出现乱码问题。网上的说法很多,经过实际测试,发现问题还在字节和字符的问题上面。
众所周知,java中分为字节流和字符流,字符(char)是16bit的,字节(byte)是8bit的。printstrean是写入一串8bit的数据的。 printwriter是写入一串16bit的数据的。
string缺省是用unicode编码,是16bit的。因此用printwriter写入的字符串,跨平台性好一些,printstream的可能会出现字符集乱码。
可以这样理解上面的话,printstream是用来操作byte, printwriter是用来操作unicode, printstream一次读8bit的话,如果遇到汉字(一个汉字占16bit),就可能会出现乱码。一般需要处理中文时用printwriter好了。
最后网站测试,使用printwriter没有出现乱码。代码如下:
java代码
import java.io.bufferedreader; import java.io.dataoutputstream; import java.io.ioexception; import java.io.outputstreamwriter; import java.io.printwriter; import java.net.socket; public class testsocket { public static void main(string[] args) throws ioexception { socket socket = new socket(); dataoutputstream dos = null; printwriter pw = null; bufferedreader in = null; string responsexml = "要传输的中文"; //.......... dos = new dataoutputstream(socket.getoutputstream()); pw = new printwriter(new outputstreamwriter(dos)); //不带自动刷新的writer pw.println(responsexml); pw.flush(); } }
需要注意的方面是,需要使用printwriter的println而不是write方法,否则服务器端会读不到数据的。原因就是println会在输出的时候在字符串后面加一个换行符,而write不会。
4.jsp中显示中文的乱码。
有的时候jsp页面在显示中文的时候会有乱码,大多数情况就是字符集配置和页面编码的问题。只要保证如下的几个配置没有问题,一般就不会有乱码出现。
a.jsp页面顶端添加如下语句:
java代码
<%@ page contenttype="text/html; charset=utf-8" language="java" errorpage="" %>
b.在html的head标签中添加如下语句。
java代码
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
c.保证jsp的页面编码与上面两个的charset相同,这点我有在文章的第一点说过。
上面的字符集可以根据需要自己灵活选择,不一定非要utf-8。不过因为utf-8对各国语言,特别是中文支持较好,所以推荐使用。我就曾经遇到过滘在gb2312编码的页面无法正常显示的问题。
5.post和get传递中文,后台获取乱码。
前台传递中文也分为get和post方法。
a.get方法的情况:
get方法的时候主要是url传递中文。
如果是在js文件中,可以使用如下代码进行中文转码。
js代码
var url ="http://www.baidu.com/s?industry=编码" url = encodeuri(url);
如果是在jsp文件中,则可以使用如下语句进行转码。
页面开始引入:
java代码
<%@ page import="java.net.urlencoder" %>
需要转码的地方使用urlencoder进行编码:
js代码
<a href="xxxxx.xx?industry=<%=urlencoder.encode(" rel="external nofollow" http://www.baidu.com/s?wd=编码", "utf-8")%>">
无论使用哪种方法,在后台获取中文的时候都要使用如下代码:
java代码
request.setcharacterencoding("utf-8"); string industry = new string( request.getparameter("industry ").getbytes("iso8859-1"),"utf-8");
【注】
1.对于request,是指提交内容的编码,指定后可以通过getparameter()则直接获得正确的字符串,如果不指定,则默认使用iso8859-1编码,为了统一,需要提交指定传输编码。
2.上面代码的第二句好像和第2条中给出的公式矛盾。我也纠结了好久,最后发现iso8859-1是一种比较老的编码,通常叫做latin-1,属于单字节编码,正好和计算机最基础的表示单位一致,因此使用它进行转码一般也没有问题。
iso-8859-1是java网络传输使用的标准字符集,而gb2312是标准中文字符集,当你作出提交表单等需要网络传输的操作的时候,就需要把 iso-8859-1转换为gb2312字符集显示,否则如果按浏览器的gb2312格式来解释iso-8859-1字符集的话,由于2者不兼容,所以会是乱码。为了省事,建议统一使用utf-8字符集。
b.post方法的情况。
对于post的情况就比较简单了,只需要在post的函数调用部分,制定post的header的字符集,如:
js代码
xmlhttp.open("post", url , true); xmlhttp.setrequestheader("content-type","text/xml; charset= utf-8"); xmlhttp.send(param);
其中param为要传递的参数。
后台部分和get方法一样,设置如下即可,注意传输和接受的字符集要统一。
6.后台向前台传递中文乱码。
在这里提供一个函数,通过这个函数来发送信息,就不会出现乱码,核心思想也是设置response流的字符集。函数代码如下:
java代码
/** * @function:writeresponse * @description:ajax方式返回字符串 * @param str:json * @return:true:输出成功,false:输出失败 */ public boolean writeresponse(string str){ boolean ret = true; try{ httpservletresponse response = servletactioncontext.getresponse(); response.setcontenttype("text/html;charset=utf-8"); printwriter pw = response.getwriter(); pw.print(str); pw.close(); }catch (exception e) { ret = false; e.printstacktrace(); } return ret; }
7.下载文件时文件名乱码。
下过下载的人都知道下载的文件容易出现乱码,原因也是没有对输出流的编码格式进行限定。
附上一段代码,用来帮你完成无乱码下载。
java代码
httpservletresponse response = servletactioncontext.getresponse(); response.setcontenttype("text/html;charset=utf-8"); response.reset(); string header = "attachment; filename=" + picname; header = new string(header.getbytes(), "utf-8"); response.setheader("content-disposition", header);
核心代码就上几句,注意第二句和第三句的reset的顺序不能搞错。
reset的作用是用来清空buffer缓存的,清空请求前部的一些空白行。
以上只是做了比较简单的总结,具体乱码有的时候可能是多个情况的组合,具体问题具体分析。如果错误欢迎指正。