1、字符編碼
在計(jì)算機(jī)中任何數(shù)據(jù)都是以二進(jìn)制存儲(chǔ)的,要存儲(chǔ)一個(gè)字符就要對(duì)它進(jìn)行編碼,用一個(gè)二進(jìn)制數(shù)與這對(duì)應(yīng),這種對(duì)應(yīng)的規(guī)則,就是字符的編碼。編碼的規(guī)則有很多 種,一種規(guī)則所編碼的“字符”的集合就叫做“字符集”。在制定編碼標(biāo)準(zhǔn)的時(shí)候,“字符的集合”和“編碼”一般都是同時(shí)制定的,因此,平時(shí)我們所說(shuō)的“字符 集”,例如GB2312、GBK和JIS等,除了有“字符的集合”這層含義外,同時(shí)也包含了“編碼”的含義。
最早出現(xiàn)的編碼是ASCII碼,因?yàn)樵缙谟?jì)算機(jī)系統(tǒng)只支持英語(yǔ)。后來(lái)每個(gè)國(guó)家(或區(qū)域)規(guī)定了計(jì)算機(jī)信息交換用的字符編碼集,例如中國(guó)的GB2312等作 為自己國(guó)家/區(qū)域內(nèi)信息處理的基礎(chǔ)。在程序讀取字符到輸出字符的過(guò)程中,就需要在不同的字符集之間進(jìn)行轉(zhuǎn)換,這個(gè)時(shí)候就容易出現(xiàn)亂碼,因此要了解亂碼是如 何產(chǎn)生的,首先要了解各種字符編碼。下面對(duì)這些編碼做一個(gè)簡(jiǎn)單介紹。
1)、ASCII
ASCII碼是《美國(guó)標(biāo)準(zhǔn)信息交換碼》,簡(jiǎn)稱ASCII,它總共規(guī)定了128個(gè)字符號(hào)所對(duì)應(yīng)的數(shù)字代碼,使用了7位二進(jìn)制的位來(lái)表示這些數(shù)字。其中包含了英文的大小寫(xiě)字母、數(shù)字和標(biāo)點(diǎn)符號(hào)常用的字符,數(shù)字代號(hào)從0~127。
2)、ISO8859
ASCII碼解決了英語(yǔ)國(guó)家的字符問(wèn)題,可是歐洲各個(gè)國(guó)家的字符問(wèn)題還沒(méi)有解決,例如法語(yǔ)中就有許多英語(yǔ)中沒(méi)有的字符,為了解決該問(wèn)題,國(guó)際標(biāo)準(zhǔn)化組織的 ISO8859標(biāo)準(zhǔn)應(yīng)運(yùn)而生,在ISO8859的編碼表中,編號(hào)0~127與ASCII保持兼容,編號(hào)128~159共32個(gè)編碼保留給擴(kuò)充定義的32個(gè) 擴(kuò)充控制碼,160為空格,161~255的95個(gè)數(shù)字用于新增加的字符代碼。由于在一張碼表中只能增加95種字符的代碼,因此ISO8859實(shí)際上不是 一張碼表,而是一系列標(biāo)準(zhǔn),包括14個(gè)字符碼表。例如,西歐的常用字符就包含在ISO8859-1字符表中,在ISO8859-7中則包含了ASCII和 現(xiàn)代希臘語(yǔ)字符。
3)、GB2312和GBK
GB2312是中國(guó)國(guó)家標(biāo)準(zhǔn)漢字信息交換用編碼,簡(jiǎn)稱國(guó)標(biāo)碼,標(biāo)準(zhǔn)號(hào)為GB2312-80。
中國(guó)的文字不是拼音文字,漢字的個(gè)數(shù)的數(shù)萬(wàn)之多,遠(yuǎn)遠(yuǎn)超過(guò)區(qū)區(qū)256個(gè)字符,ISO8859無(wú)能為力,但是通過(guò)借鑒ISO8859的編碼思想,研 究人員解決了中文的編碼問(wèn)題。GB2312使用兩個(gè)字節(jié)來(lái)表示一個(gè)中文,在每個(gè)字符的256種可能中,為了與ASCII保持兼容,低于128的我們不使 用。借鑒ISO8859的設(shè)計(jì)方案,只使用從160以后的96個(gè)數(shù)字,兩個(gè)字節(jié)分成高位和低位,高位的取值范圍從176~247共72個(gè),低位從 161~254共94個(gè),這樣,兩個(gè)字節(jié)就有72*94=6768種可能,即可表示6768種漢字。
BG2312-80僅收錄了6763個(gè)漢字,還有許多漢字沒(méi)有被收錄進(jìn)去,為了對(duì)更多的字符進(jìn)行編碼,全國(guó)信息技術(shù)化技術(shù)委員會(huì)于1995年12月1日頒 布了《漢字內(nèi)碼擴(kuò)展規(guī)范》,簡(jiǎn)稱GBK。在GBK1.0中共收錄了21886個(gè)漢字和圖形符號(hào),微軟公司的window95系統(tǒng)的簡(jiǎn)體中文版開(kāi)始即支持 GBK編碼。GBK向下與GB2312完全兼容,向上支持ISO10646國(guó)際標(biāo)準(zhǔn)。
(4)、UNICODE
每個(gè)國(guó)家和地區(qū)都規(guī)定了計(jì)算機(jī)信息交換編碼,這就造成了不同編碼國(guó)家、地區(qū)之間交流上的困難,如果全世界都使用統(tǒng)一的編碼表就好了,為此UNICODE組 織發(fā)布了UNICODE編碼。這種編碼使用雙字節(jié)符號(hào)數(shù)對(duì)每一個(gè)字符進(jìn)行編碼,在UNICODE3.0.1中包含了49194個(gè)字符,將 來(lái),UNICODE中還會(huì)增加更多的字符。UNICODE的全稱是“Universal Multiple-Octet Coded Character Set”,簡(jiǎn)稱UCS。
(5)、UTF-8
使用UNICODE編碼,一個(gè)英文字符也要占據(jù)兩個(gè)字節(jié),對(duì)于英文信息而言就增加了一倍數(shù)據(jù)量,為了減少存儲(chǔ)和傳輸英文數(shù)據(jù)的數(shù)據(jù)量,美國(guó)人又制定了一系 列用于傳輸和保存UNICODE的編碼標(biāo)準(zhǔn)UTF,這些編碼稱為UCS傳輸格式碼,也就是將UCS的編碼通過(guò)一定的轉(zhuǎn)換來(lái)達(dá)到使用的目的。常見(jiàn)的有 UTF-7、UTF-8、UTF-16等。其中UTF-8編碼得到了廣泛的應(yīng)用,UTF-8的全名是UCS Transformation Format 8,即UCS編碼的8位傳輸格式,就是使用單字節(jié)的方式對(duì)UCS進(jìn)行編碼,使UNICODE編碼能夠在單字節(jié)的設(shè)備上正常進(jìn)行處理。
UTF-8編碼是變長(zhǎng)的編碼,對(duì)不同的UNICODE可能編成不同的長(zhǎng)度。
從理論上來(lái)說(shuō),這些根據(jù)字符集設(shè)置而進(jìn)行的字符轉(zhuǎn)換不應(yīng)該產(chǎn)生太多的問(wèn)題,而事實(shí)上由于應(yīng)用程序的實(shí)際運(yùn)行環(huán)境不同,UNICODE和各個(gè)本地字符集的補(bǔ)充、完善以及系統(tǒng)或應(yīng)用程序?qū)崿F(xiàn)的不規(guī)范,轉(zhuǎn)碼時(shí)還是會(huì)經(jīng)常出現(xiàn)問(wèn)題而導(dǎo)致亂碼。
2、亂碼產(chǎn)生的原因
Java語(yǔ)言內(nèi)部是用UNICODE表示字符的,遵守UNICODE V2.0。Java程序無(wú)論是以字符流讀/寫(xiě)磁盤文件,還是向URL轉(zhuǎn)接寫(xiě)HTML信息,或從URL連接讀取參數(shù)值,都會(huì)由UNICODE作為中介和本地字符編碼進(jìn)行轉(zhuǎn)換。
在WEB應(yīng)用中,瀏覽器、WEB服務(wù)器、WEB應(yīng)用程序和數(shù)據(jù)庫(kù)等各個(gè)部分都有可能使用不用的字符集,字符在不同的字符集之間進(jìn)行轉(zhuǎn)換時(shí),就有可能出現(xiàn)亂碼問(wèn)題。例如一個(gè)中文字符“中”要轉(zhuǎn)換為ISO-8859-1編碼,在Java中先讀取到的是中文字符的GBK編碼“0xD6D0”,轉(zhuǎn)換為 UNICODE碼為“0x4E2D”,再?gòu)腢NICODE編碼轉(zhuǎn)換ISO-8859-1編碼,如果ISO-8859-1編碼中沒(méi)有對(duì)應(yīng)的 “0xD6D0”,于是就得到“0x3F”,也就是我們經(jīng)常在頁(yè)面上看到一堆“?”的原因。
在WEB應(yīng)用中,亂碼可能在多個(gè)環(huán)節(jié)產(chǎn)生,下面針對(duì)可能出現(xiàn)亂碼的幾個(gè)環(huán)節(jié),給出解決的方案。
3、亂碼解決方案
1)、JSP頁(yè)面最基本的亂碼問(wèn)題
運(yùn)行下面的代碼,會(huì)發(fā)現(xiàn)頁(yè)面上出現(xiàn)的是亂碼。
1. <%@ page language="java" pageEncoding="UTF-8"%>
2. <%@ page contentType="text/html; charset=ISO8859-1"%>
3. <html>
4. <head>
5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6. <title>中文問(wèn)題</title>
7.
8. <body>
9. </body>
10.</html>
這段代碼產(chǎn)生亂碼的原因在于它有三處設(shè)置了編碼格式,但是格式設(shè)置的不一致,因此導(dǎo)致了亂碼,下面來(lái)分析這三處設(shè)置編碼格式的作用。
<%@ page language="java" pageEncoding="UTF-8"%>
此處的pageEncoding="UTF-8"為JSP文件的存儲(chǔ)格式。
<%@ page contentType="text/html; charset=ISO8859-1"%>
此處charset=ISO8859-1為JSP的解碼格式。ISO8859-1是沒(méi)有為漢字編碼的,因此按UTF-8編碼存儲(chǔ)的文件如果用 ISO8859-1編碼格式編碼,其中的中文字符就會(huì)因?yàn)檎也坏綄?duì)應(yīng)的編碼而顯示為亂碼。所以JSP文件的存儲(chǔ)格式和它的解碼格式應(yīng)該是一致的。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
此處編碼控制的是瀏覽器的解碼方式,瀏覽器收到的只是一個(gè)字節(jié)流,它并不知道
頁(yè)面是如何編碼的,因此,需要一個(gè)機(jī)制來(lái)告訴瀏覽器頁(yè)面的編碼類型,標(biāo)準(zhǔn)的機(jī)制是使用<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">來(lái)指定頁(yè)面的編碼,當(dāng)瀏覽器讀取頁(yè)面遇到這樣的指示時(shí),將使用這里制定的編碼方式重新加載頁(yè)面。
因此只要把三處的都設(shè)置為UTF-8或GBK即可解決亂碼問(wèn)題。
2)、表單使用POST提交方式提交后接收到的是亂碼
WEB容器在內(nèi)部編碼格式是ISO8859-1,在POST方式提交時(shí),默認(rèn)的提交編碼格式是ISO8859-1,這樣接收到的中文信息就會(huì)是亂碼,解決方式如下:
在請(qǐng)求頁(yè)面上開(kāi)始處,執(zhí)行請(qǐng)求和編碼代碼:
request.setCharacterEncoding(“GBK”);
把提交內(nèi)容的字符集設(shè)為GBK,這樣接受此參數(shù)的頁(yè)面就不必再轉(zhuǎn)碼了,直接使用即可得到漢字參數(shù)。
在每一個(gè)接受提交的JSP頁(yè)面及Servlet中都加這樣的代碼是比較煩人的,可以用過(guò)濾器設(shè)置request和response的setCharacterEncoding方法來(lái)解決這個(gè)問(wèn)題。
例:過(guò)濾器解決問(wèn)題
1. public class setEncodeingFilter extends HttpServlet implements Filter {
2. private FilterConfig config;
3. @Override
4. public void doFilter(ServletRequest request, ServletResponse response,
5. FilterChain chain) throws IOException, ServletException {
6. request.setCharacterEncoding("GBK");
7. response.setCharacterEncoding("GBK");
8. chain.doFilter(request, response);
9. }
10.
11. @Override
12. public void init(FilterConfig config) throws ServletException {
13. this.config = config;
14. }
15.}
這個(gè)過(guò)濾器中已經(jīng)在doFilter方法中直接設(shè)置了統(tǒng)一使用GBK編碼,然后在web.xml國(guó)配置過(guò)濾器:
1. <filter>
2. <filter-name>setEncodeingFilter </filter-name>
3. <filter-class>setEncodeingFilter(過(guò)濾器的全路徑)</filter-class>
4. </filter>
5. <filter-mapping>
6. <filter-name>setEncodeingFilter </filter-name>
7. <url-pattern>/*</url-pattern>
8. </filter-mapping>
9. <url-pattern>/*</url-pattern>表示所有的頁(yè)面都要經(jīng)過(guò)此過(guò)濾器過(guò)濾,這樣就不用在JSP和Servlet中設(shè)置encoding了。
3)、表單使用GET方式導(dǎo)致的亂碼的處理方式
如果使用GET方式提交中文,接受參數(shù)的頁(yè)面也會(huì)出現(xiàn)亂碼,原因是web容器會(huì)以GET方式的默認(rèn)編碼方式ISO8859-1對(duì)漢字進(jìn)行編碼,編 碼后追加到URL,導(dǎo)致接受頁(yè)面得到的參數(shù)為亂碼。 因?yàn)樵谶M(jìn)入U(xiǎn)RL之前已經(jīng)進(jìn)行了ISO8859-1的編碼處理,所以需要在得到參數(shù)值后進(jìn)行編碼轉(zhuǎn)換:
String name = request.getParameter("name");
name = new String(name.getBytes("ISO8859-1"),"GBK");
4)、數(shù)據(jù)庫(kù)中讀取和存儲(chǔ)中文時(shí)的亂碼問(wèn)題
大多數(shù)的JDBC驅(qū)動(dòng)都是默認(rèn)IS-O8859-1為數(shù)據(jù)的傳輸編碼格式,而數(shù)據(jù)庫(kù)本身又有自己的字符集,因此在數(shù)據(jù)庫(kù)讀寫(xiě)中文數(shù)據(jù)庫(kù)時(shí)也經(jīng)常會(huì)出現(xiàn)籌碼人。
流行的關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)都支持?jǐn)?shù)據(jù)庫(kù)Encoding,即在創(chuàng)建數(shù)據(jù)庫(kù)時(shí)可以指定其自己的字符集設(shè)置。數(shù)據(jù)庫(kù)的數(shù)據(jù)以指定的編碼形式存儲(chǔ)。當(dāng)應(yīng)用程序訪問(wèn)數(shù)據(jù)時(shí),在入口和出口處都會(huì)有Encoding轉(zhuǎn)換。對(duì)于中文數(shù)據(jù),數(shù)據(jù)庫(kù)字符編碼的設(shè)置應(yīng)當(dāng)保證數(shù)據(jù)的完整性。
GB23212、GBK和UTF-8等都是可選的數(shù)據(jù)庫(kù)Encoding,在JSP/Servlet編程時(shí),可以先用數(shù)據(jù)庫(kù)管理系統(tǒng)提供的管理功能檢查其中的中文數(shù)據(jù)是否正確。
3.1、解決HTML頁(yè)面中的中文問(wèn)題:
為了使HTML頁(yè)面很好地支持中文,就必須在每個(gè)HTML頁(yè)面的頭部增加如下代碼:
1. <HEAD>
2. ...
3. <META http-equiv=Content-Type content="text/html;charset=GBK">
4. ...
5. <HEAD>
3.2、解決JSP頁(yè)面中的中文問(wèn)題
為了使JSP頁(yè)面很好地支持中文,就必須在每個(gè)JSP頁(yè)面的頭部增加如下代碼:
<%@ page contentType="text/html;charset=GBK" language="java"%>
3.3、解決Servlet響應(yīng)結(jié)果的中文問(wèn)題
為了使Servlet頁(yè)面很好地支持中文,就必須在每個(gè)Servlet頁(yè)面的頭部增加如下代碼:
response.setCharacterEncoding("GBK");
3.4解決頁(yè)面數(shù)據(jù)傳輸?shù)闹形膯?wèn)題
為了使中文數(shù)據(jù)在各頁(yè)面(組件)之間正常傳遞,最佳的方法就是采用編碼過(guò)濾器來(lái)解決。在WEB.XML中配置一個(gè)編碼過(guò)濾器,內(nèi)容如下:
1. <!--定義編碼過(guò)濾器-->
2.
3. <filter>
4. <filter-name>encodingFilter</filter-name>
5. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
6. <init-param>
7. <param-name>encoding</param-name>
8. <param-value>GBK</param-value>
9. </init-param>
10.</filter>
11.<filter-mapping>
12. <filter-name>encodingFilter</filter-name>
13. <url-pattern>/*</url-pattern>
14.</filter-mapping>
3.5、解決HTTP(get)請(qǐng)求中的中文問(wèn)題
在默認(rèn)情況下,IE瀏覽器發(fā)“ISO-8859-1”的編碼格式發(fā)送請(qǐng)求,如果接收到HTTP的get請(qǐng)求中文參數(shù)時(shí)出現(xiàn)亂碼,就可以對(duì)其進(jìn)行編碼轉(zhuǎn)換,例如:
String param = request.getParameter("param");
param = new String(param.getBytes("ISO-8859-1", "GBK"));
也可以通過(guò)修改Tomcat的server.xml文件來(lái)解決:
<Connector port = "8080"
.....
URIEncoding="GBK"/> -->增加這項(xiàng)
3.6、解決MySQL數(shù)據(jù)庫(kù)的中文問(wèn)題
解決MySQL數(shù)據(jù)庫(kù)中文問(wèn)題主要在JDBC驅(qū)動(dòng)的URL上,例如:
jdbc:mysql://localhost/test?user=root&password=123456&useUnicode=true&characterEncoding=GBK
3.7、實(shí)現(xiàn)加解密過(guò)程中,報(bào)文是亂碼:設(shè)置編碼格式可能有誤。
3.8、在本地測(cè)試是正常的,但是發(fā)布到測(cè)試環(huán)境后會(huì)亂碼的問(wèn)題
1)可能是編譯不一致,比如,測(cè)試環(huán)境要求編譯成1.4版本,而本地編譯的是1.6;
2)可能請(qǐng)求報(bào)文的編碼和服務(wù)器編碼不同,服務(wù)器是UTF-8的編碼,而請(qǐng)求報(bào)文是GBK。
該文章在 2023/8/18 11:53:47 編輯過(guò)