Java亂碼問(wèn)題解決方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
1.文件頁(yè)面編碼導(dǎo)致的亂碼。
每一個(gè)文件(java,js,jsp,html等)都有其本身的編碼格式,文件中的代碼在一種編碼中顯示正常,在另外一種編碼下就會(huì)顯示出亂碼。
在Eclipse中,每一個(gè)工程都會(huì)有編碼格式(Text file encoding), 一般默認(rèn)為GBK。而一個(gè)比較好的編程習(xí)慣是新建一個(gè)項(xiàng)目,優(yōu)先把項(xiàng)目的編碼設(shè)為UTF-8。
這樣做的原因很簡(jiǎn)單,UTF-8包含全世界所有國(guó)家需要用到的字符,是國(guó)際編碼,通用性強(qiáng)。幾種常見(jiàn)的字符集,GBK,GB2312,UTF-8之間的關(guān)系如下:
GBK是國(guó)家標(biāo)準(zhǔn)GB2312基礎(chǔ)上擴(kuò)容后兼容GB2312的標(biāo)準(zhǔn)。GBK、GB2312等與UTF8之間都必須通過(guò)Unicode編碼才能相互轉(zhuǎn)換
2.不同字符集的字符串轉(zhuǎn)換時(shí)導(dǎo)致的亂碼。
每一個(gè)String,底層實(shí)現(xiàn)都是用一個(gè)byte數(shù)組存儲(chǔ),使用不同的字符集,存儲(chǔ)的數(shù)組長(zhǎng)度當(dāng)然就不同。如果不使用同一種字符集進(jìn)行解碼,就一定會(huì)出現(xiàn)亂碼。
例如如下代碼:
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("系統(tǒng)默認(rèn)的字符集是:" + 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); } }
輸出結(jié)果為:
Java代碼
1.系統(tǒng)默認(rèn)的字符集是:UTF-8
2.4
3.6
4.6
可以看出,使用GBK和UTF-8編碼,得到的byte數(shù)組長(zhǎng)度不一樣,原因就是utf-8使用3個(gè)字節(jié)來(lái)編碼中文,而GBK使用2個(gè)字節(jié)來(lái)編碼中文。因?yàn)槲业捻?xiàng)目默認(rèn)使用UTF-8,所以使用不加參數(shù)的getBytes()得到的數(shù)組長(zhǎng)度和使用UTF-8編碼的 字符串長(zhǎng)度一樣。關(guān)于字符集的詳細(xì)知識(shí)可以參考第一部分中給出的文章地址。
JDK中關(guān)于getBytes方法的描述:
getBytes() 使用平臺(tái)的默認(rèn)字符集將此 String 編碼為 byte 序列,并將結(jié)果存儲(chǔ)到一個(gè)新的 byte 數(shù)組中。
getBytes(Charset charset) 使用給定的 charset 將此 String 編碼到 byte 序列,并將結(jié)果存儲(chǔ)到新的 byte 數(shù)組。
每一個(gè)字符串底層都有自己的編碼方式。不過(guò)一旦調(diào)用getByte方法后,得到的byte數(shù)組就是使用某種特定字符集編碼后的數(shù)組,不需要再做多余的轉(zhuǎn)換。
當(dāng)?shù)玫缴厦娴腷yte數(shù)組后,就可以調(diào)用String的另外一個(gè)方法來(lái)生成需要轉(zhuǎn)碼的String了。
測(cè)試?yán)尤缦拢?/p>
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")); } }
輸出結(jié)果為:
Java代碼
1.中文
2.����
3.**************************
4.中文
5.涓枃
可以看出,使用哪種字符集編碼一個(gè)String,在生成一個(gè)String的時(shí)候就必須使用相應(yīng)的編碼,否則就會(huì)出現(xiàn)亂碼。
簡(jiǎn)單來(lái)講,只有滿(mǎn)足如下公式的String轉(zhuǎn)碼,才不會(huì)亂碼。
Java代碼
String strSource = "你想要轉(zhuǎn)碼的字符串"; String strSomeEncoding = "utf-8"; //例如utf-8 String strTarget = new String (strSource.getBytes(Charset.forName(strSomeEncoding)), strSomeEncoding);
JDK中關(guān)于getBytes方法的描述:
String(byte[] bytes) 通過(guò)使用平臺(tái)的默認(rèn)字符集解碼指定的 byte 數(shù)組,構(gòu)造一個(gè)新的 String。
String(byte[] bytes, Charset charset) 通過(guò)使用指定的 charset 解碼指定的 byte 數(shù)組,構(gòu)造一個(gè)新的 String。
3.Socket網(wǎng)絡(luò)傳輸時(shí)導(dǎo)致的中文亂碼。
使用Socket進(jìn)行通訊的時(shí)候,傳輸有多種選擇,可以使用PrintStream,也可以使用PrintWriter。傳輸英文還好,傳輸中文就可能出現(xiàn)亂碼問(wèn)題。網(wǎng)上的說(shuō)法很多,經(jīng)過(guò)實(shí)際測(cè)試,發(fā)現(xiàn)問(wèn)題還在字節(jié)和字符的問(wèn)題上面。
眾所周知,Java中分為字節(jié)流和字符流,字符(char)是16bit的,字節(jié)(BYTE)是8bit的。PrintStrean是寫(xiě)入一串8bit的數(shù)據(jù)的。 PrintWriter是寫(xiě)入一串16bit的數(shù)據(jù)的。
String缺省是用UNICODE編碼,是16bit的。因此用PrintWriter寫(xiě)入的字符串,跨平臺(tái)性好一些,PrintStream的可能會(huì)出現(xiàn)字符集亂碼。
可以這樣理解上面的話,PrintStream是用來(lái)操作byte, PrintWriter是用來(lái)操作Unicode, PrintStream一次讀8bit的話,如果遇到漢字(一個(gè)漢字占16bit),就可能會(huì)出現(xiàn)亂碼。一般需要處理中文時(shí)用PrintWriter好了。
最后網(wǎng)站測(cè)試,使用PrintWriter沒(méi)有出現(xiàn)亂碼。代碼如下:
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 = "要傳輸?shù)闹形?; //.......... dos = new DataOutputStream(socket.getOutputStream()); pw = new PrintWriter(new OutputStreamWriter(dos)); //不帶自動(dòng)刷新的Writer pw.println(responseXml); pw.flush(); } }
需要注意的方面是,需要使用PrintWriter的println而不是write方法,否則服務(wù)器端會(huì)讀不到數(shù)據(jù)的。原因就是println會(huì)在輸出的時(shí)候在字符串后面加一個(gè)換行符,而write不會(huì)。
4.JSP中顯示中文的亂碼。
有的時(shí)候JSP頁(yè)面在顯示中文的時(shí)候會(huì)有亂碼,大多數(shù)情況就是字符集配置和頁(yè)面編碼的問(wèn)題。只要保證如下的幾個(gè)配置沒(méi)有問(wèn)題,一般就不會(huì)有亂碼出現(xiàn)。
a.JSP頁(yè)面頂端添加如下語(yǔ)句:
Java代碼
<%@ page contentType="text/html; charset=utf-8" language="java" errorPage="" %>
b.在HTML的head標(biāo)簽中添加如下語(yǔ)句。
Java代碼
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
c.保證JSP的頁(yè)面編碼與上面兩個(gè)的charset相同,這點(diǎn)我有在文章的第一點(diǎn)說(shuō)過(guò)。
上面的字符集可以根據(jù)需要自己靈活選擇,不一定非要utf-8。不過(guò)因?yàn)閡tf-8對(duì)各國(guó)語(yǔ)言,特別是中文支持較好,所以推薦使用。我就曾經(jīng)遇到過(guò)滘在GB2312編碼的頁(yè)面無(wú)法正常顯示的問(wèn)題。
5.Post和Get傳遞中文,后臺(tái)獲取亂碼。
前臺(tái)傳遞中文也分為Get和Post方法。
a.Get方法的情況:
Get方法的時(shí)候主要是URL傳遞中文。
如果是在js文件中,可以使用如下代碼進(jìn)行中文轉(zhuǎn)碼。
Js代碼
var url ="http://www.baidu.com/s?industry=編碼" url = encodeURI(url);
如果是在jsp文件中,則可以使用如下語(yǔ)句進(jìn)行轉(zhuǎn)碼。
頁(yè)面開(kāi)始引入:
Java代碼
<%@ page import="java.net.URLEncoder" %>
需要轉(zhuǎn)碼的地方使用URLEncoder進(jìn)行編碼:
Js代碼
<a href="xxxxx.xx?industry=<%=URLEncoder.encode(" rel="external nofollow" http://www.baidu.com/s?wd=編碼", "UTF-8")%>">
無(wú)論使用哪種方法,在后臺(tái)獲取中文的時(shí)候都要使用如下代碼:
Java代碼
request.setCharacterEncoding("utf-8"); String industry = new String( request.getParameter("industry ").getBytes("ISO8859-1"),"UTF-8");
【注】
1.對(duì)于request,是指提交內(nèi)容的編碼,指定后可以通過(guò)getParameter()則直接獲得正確的字符串,如果不指定,則默認(rèn)使用iso8859-1編碼,為了統(tǒng)一,需要提交指定傳輸編碼。
2.上面代碼的第二句好像和第2條中給出的公式矛盾。我也糾結(jié)了好久,最后發(fā)現(xiàn)ISO8859-1是一種比較老的編碼,通常叫做Latin-1,屬于單字節(jié)編碼,正好和計(jì)算機(jī)最基礎(chǔ)的表示單位一致,因此使用它進(jìn)行轉(zhuǎn)碼一般也沒(méi)有問(wèn)題。
iso-8859-1是JAVA網(wǎng)絡(luò)傳輸使用的標(biāo)準(zhǔn)字符集,而gb2312是標(biāo)準(zhǔn)中文字符集,當(dāng)你作出提交表單等需要網(wǎng)絡(luò)傳輸?shù)牟僮鞯臅r(shí)候,就需要把 iso-8859-1轉(zhuǎn)換為gb2312字符集顯示,否則如果按瀏覽器的gb2312格式來(lái)解釋iso-8859-1字符集的話,由于2者不兼容,所以會(huì)是亂碼。為了省事,建議統(tǒng)一使用utf-8字符集。
b.POST方法的情況。
對(duì)于Post的情況就比較簡(jiǎn)單了,只需要在post的函數(shù)調(diào)用部分,制定post的header的字符集,如:
Js代碼
xmlHttp.open("post", url , true); xmlHttp.setRequestHeader("Content-Type","text/xml; charset= utf-8"); xmlHttp.send(param);
其中param為要傳遞的參數(shù)。
后臺(tái)部分和get方法一樣,設(shè)置如下即可,注意傳輸和接受的字符集要統(tǒng)一。
6.后臺(tái)向前臺(tái)傳遞中文亂碼。
在這里提供一個(gè)函數(shù),通過(guò)這個(gè)函數(shù)來(lái)發(fā)送信息,就不會(huì)出現(xiàn)亂碼,核心思想也是設(shè)置response流的字符集。函數(shù)代碼如下:
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.下載文件時(shí)文件名亂碼。
下過(guò)下載的人都知道下載的文件容易出現(xiàn)亂碼,原因也是沒(méi)有對(duì)輸出流的編碼格式進(jìn)行限定。
附上一段代碼,用來(lái)幫你完成無(wú)亂碼下載。
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的順序不能搞錯(cuò)。
reset的作用是用來(lái)清空buffer緩存的,清空請(qǐng)求前部的一些空白行。
以上只是做了比較簡(jiǎn)單的總結(jié),具體亂碼有的時(shí)候可能是多個(gè)情況的組合,具體問(wèn)題具體分析。如果錯(cuò)誤歡迎指正。
相關(guān)文章
Apache Commons Math3學(xué)習(xí)之?dāng)?shù)值積分實(shí)例代碼
這篇文章主要介紹了Apache Commons Math3學(xué)習(xí)之?dāng)?shù)值積分實(shí)例代碼,涉及使用辛普森積分的例子,這里分享給大家,供需要的朋友參考。2017-10-10mybatis如何通過(guò)接口查找對(duì)應(yīng)的mapper.xml及方法執(zhí)行詳解
這篇文章主要給大家介紹了利用mybatis如何通過(guò)接口查找對(duì)應(yīng)的mapper.xml及方法執(zhí)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-06-06Java實(shí)現(xiàn)簡(jiǎn)易俄羅斯方塊
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易俄羅斯方塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06Spring Boot配置動(dòng)態(tài)更新問(wèn)題
這篇文章主要介紹了Spring Boot配置動(dòng)態(tài)更新問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java圖形化編程之JFrame疫苗接種系統(tǒng)詳解
GUI圖形界面設(shè)計(jì)是用戶(hù)和程序交互的工具,用戶(hù)通過(guò)圖形界面控制程序事件的發(fā)生。首先介紹Swing的基本體系結(jié)構(gòu),這是底層2021-09-09MybatisPlusInterceptor實(shí)現(xiàn)sql攔截器超詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于MybatisPlusInterceptor實(shí)現(xiàn)sql攔截器超詳細(xì)教程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08Spring源碼BeanFactoryPostProcessor詳解
BeanFactoryPostProcessor的執(zhí)行時(shí)機(jī)是在Spring掃描完成后,Bean初始化前,當(dāng)我們實(shí)現(xiàn)BeanFactoryPostProcessor接口,可以在Bean的初始化之前對(duì)Bean進(jìn)行屬性的修改,下面通過(guò)本文看下Spring源碼分析-BeanFactoryPostProcessor的實(shí)例代碼,感興趣的朋友一起看看吧2021-11-11Gradle構(gòu)建多模塊項(xiàng)目的方法步驟
這篇文章主要介紹了Gradle構(gòu)建多模塊項(xiàng)目的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05