您好,登錄后才能下訂單哦!
一、JavaWeb編程中亂碼的成因
因?yàn)橛?jì)算機(jī)只認(rèn)識(shí)0與1,在網(wǎng)絡(luò)上要想傳輸各種字符就需要進(jìn)行編碼。而由于編碼、傳輸、解碼過(guò)程存在各種不確定性,導(dǎo)致亂碼問(wèn)題頻發(fā),成為困擾初學(xué)者的一大問(wèn)題。本文就試圖用最簡(jiǎn)單的示例解釋亂碼問(wèn)題。
1.為什么會(huì)出現(xiàn)亂碼問(wèn)題
如同發(fā)電報(bào)一樣,如果發(fā)報(bào)的采用一個(gè)密碼本進(jìn)行發(fā)報(bào),而接收端采用另外的密碼本進(jìn)行解碼,肯定會(huì)導(dǎo)致無(wú)法解碼一樣。如果在計(jì)算機(jī)網(wǎng)絡(luò)中傳輸數(shù)據(jù),發(fā)送端采用的編碼和接收端采用的編碼不一致就會(huì)導(dǎo)致亂碼問(wèn)題。
2.認(rèn)識(shí)各種編碼:
ASCII:
ASCII(American Standard Code for Information Interchange,美國(guó)信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母的一套電腦編碼系統(tǒng),主要用于顯示現(xiàn)代英語(yǔ)和其他西歐語(yǔ)言。它是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng),并等同于國(guó)際標(biāo)準(zhǔn)ISO/IEC 646。
ISO-8859-1:
由于ASCII是美國(guó)標(biāo)準(zhǔn),其收錄了空格及94個(gè)“可印刷字符”,足以給英語(yǔ)使用。但是,其他使用拉丁字母的語(yǔ)言(主要是歐洲國(guó)家的語(yǔ)言),都有一定數(shù)量的附加符號(hào)字母,故需要使用ASCII及控制字符以外的區(qū)域來(lái)儲(chǔ)存及表示。
為了解決這個(gè)問(wèn)題,國(guó)際標(biāo)準(zhǔn)化組織(ISO)及國(guó)際電工委員會(huì)(IEC)聯(lián)合制定的一系列8位字符集的標(biāo)準(zhǔn)ISO-8859,其全稱ISO/IEC 8859,現(xiàn)時(shí)定義了15個(gè)字符集。除了使用拉丁字母的語(yǔ)言外,使用西里爾字母的東歐語(yǔ)言、希臘語(yǔ)、泰語(yǔ)、現(xiàn)代阿拉伯語(yǔ)、希伯來(lái)語(yǔ)等,都可以使用這個(gè)形式來(lái)儲(chǔ)存及表示。
GBK/GB2312:
為了解決中文的顯示和編輯問(wèn)題,1980年中國(guó)國(guó)家標(biāo)準(zhǔn)總局發(fā)布了GB2312標(biāo)準(zhǔn),全稱是《信息交換用漢字編碼字符集》,標(biāo)準(zhǔn)號(hào)是GB 2312—1980。GB2312編碼適用于漢字處理、漢字通信等系統(tǒng)之間的信息交換,通行于中國(guó)大陸;新加坡等地也采用此編碼。中國(guó)大陸幾乎所有的中文系統(tǒng)和國(guó)際化的軟件都支持GB 2312?;炯彩杖霛h字6763個(gè)和非漢字圖形字符682個(gè)。GB 2312的出現(xiàn),基本滿足了漢字的計(jì)算機(jī)處理需要,它所收錄的漢字已經(jīng)覆蓋中國(guó)大陸99.75%的使用頻率。但是對(duì)于人名、古漢語(yǔ)等方面出現(xiàn)的罕用字,GB 2312不能處理,這導(dǎo)致了后來(lái)GBK及GB 18030漢字字符集的出現(xiàn)。
1995年中國(guó)國(guó)家標(biāo)準(zhǔn)總局又頒布了《漢字編碼擴(kuò)展規(guī)范》(GBK)。GBK與GB 2312—1980國(guó)家標(biāo)準(zhǔn)所對(duì)應(yīng)的內(nèi)碼標(biāo)準(zhǔn)兼容,同時(shí)在字匯一級(jí)支持ISO/IEC10646—1和GB 13000—1的全部中、日、韓(CJK)漢字,共計(jì)20902字。
國(guó)家標(biāo)準(zhǔn)GB18030-2005《信息技術(shù) 中文編碼字符集》是我國(guó)繼GB2312-1980和GB13000.1-1993之后最重要的漢字編碼標(biāo)準(zhǔn),是我國(guó)計(jì)算機(jī)系統(tǒng)必須遵循的基礎(chǔ)性標(biāo)準(zhǔn)之一。 GB18030有兩個(gè)版本:GB18030-2000和GB18030-2005。GB18030-2000是GBK的取代版本,它的主要特點(diǎn)是在GBK基礎(chǔ)上增加了CJK統(tǒng)一漢字?jǐn)U充A的漢字。GB18030-2005的主要特點(diǎn)是在GB18030-2000基礎(chǔ)上增加了CJK統(tǒng)一漢字?jǐn)U充B的漢字。GB18030-2005是我國(guó)自主研制的以漢字為主并包含多種我國(guó)少數(shù)民族文字(如藏、蒙古、傣、彝、朝鮮、維吾爾文等)的超大型中文編碼字符集強(qiáng)制性標(biāo)準(zhǔn),其中收入漢字70000余個(gè)。中文的Windows操作系統(tǒng)默認(rèn)使用GBK/GB2312編碼。
Unicode:
很多傳統(tǒng)的編碼方式都有一個(gè)共同的問(wèn)題,即容許電腦處理雙語(yǔ)環(huán)境(通常使用拉丁字母以及其本地語(yǔ)言),但卻無(wú)法同時(shí)支持多語(yǔ)言環(huán)境(指可同時(shí)處理多種語(yǔ)言混合的情況)。Unicode 是為了解決傳統(tǒng)的字符編碼方案的局限而產(chǎn)生的,例如ISO 8859所定義的字符雖然在不同的國(guó)家中廣泛地使用,可是在不同國(guó)家間卻經(jīng)常出現(xiàn)不兼容的情況。Unicode的出現(xiàn),能夠使計(jì)算機(jī)實(shí)現(xiàn)跨語(yǔ)言、跨平臺(tái)的文本轉(zhuǎn)換及處理。
UTF-8:
因?yàn)閁nicode編碼使用2個(gè)字節(jié)存儲(chǔ)一個(gè)字符。事實(shí)證明,對(duì)可以用ASCII表示的字符使用Unicode并不高效,因?yàn)閁nicode比ASCII占用大一倍的空間,而對(duì)ASCII來(lái)說(shuō)高字節(jié)的0對(duì)他毫無(wú)用處。為了解決這個(gè)問(wèn)題,就出現(xiàn)了一些中間格式的字符集,他們被稱為通用轉(zhuǎn)換格式,即UTF(Unicode Transformation Format)。
UTF-8(8-bit Unicode Transformation Format)是一種針對(duì)Unicode的可變長(zhǎng)度字符編碼。由Ken Thompson于1992年創(chuàng)建?,F(xiàn)在已經(jīng)標(biāo)準(zhǔn)化為RFC 3629。UTF-8用1到4個(gè)字節(jié)編碼UNICODE字符。用在網(wǎng)頁(yè)上可以同一頁(yè)面顯示中文簡(jiǎn)體繁體及其它語(yǔ)言(如英文,日文,韓文)。
UTF-16:
UTF-16是Unicode字符編碼五層次模型的第三層:字符編碼表(Character Encoding Form,也稱為 "storage format")的一種實(shí)現(xiàn)方式。即把Unicode字符集的抽象碼位映射為16位長(zhǎng)的整數(shù)(即碼元)的序列,用于數(shù)據(jù)存儲(chǔ)或傳遞。Unicode字符的碼位,需要1個(gè)或者2個(gè)16位長(zhǎng)的碼元來(lái)表示,因此這是一個(gè)定長(zhǎng)表示。Java語(yǔ)言默認(rèn)使用UTF-16作為內(nèi)存的字符存儲(chǔ)格式。
二、各種亂碼的處理
亂碼的處理問(wèn)題因?yàn)楫a(chǎn)生原因不同,需要使用不同的方式進(jìn)行處理,現(xiàn)就不同的問(wèn)題分情況討論如下:
1.響應(yīng)中的亂碼處理
A.使用字節(jié)輸出流輸出響應(yīng):
Stringdata="傳智播客";
response.getOutputStream().write(data.getBytes());
經(jīng)實(shí)測(cè)不會(huì)發(fā)生亂碼問(wèn)題,原因是String的getBytes()方法默認(rèn)使用本地平臺(tái)默認(rèn)編碼將字符串轉(zhuǎn)換為字節(jié)數(shù)組,而我們中文的windows操作系統(tǒng)默認(rèn)使用GBK編碼。而在用戶端,瀏覽器默認(rèn)也使用操作系統(tǒng)默認(rèn)編碼解析網(wǎng)頁(yè),在windows系統(tǒng)下默認(rèn)也是GBK。發(fā)送端及接收端編碼一致,所以不會(huì)產(chǎn)生亂碼問(wèn)題。
由于UTF-8編碼是國(guó)際上通用的編碼,所以我們?cè)谧鯳eb開(kāi)發(fā)時(shí),通常使用UTF-8編碼。但是如果使用getBytes("UTF-8")得到字節(jié)數(shù)組,并發(fā)送給客戶端,此時(shí)會(huì)產(chǎn)生亂碼問(wèn)題。原因是因?yàn)間etBytes("UTF-8")實(shí)際是指定按照UTF-8編碼將字符串轉(zhuǎn)換成字節(jié)數(shù)組。而瀏覽器此時(shí)默認(rèn)仍然使用本地平臺(tái)默認(rèn)編碼進(jìn)行解碼就會(huì)發(fā)生亂碼問(wèn)題。
此時(shí)的顯示效果如下:
此時(shí)可以使用以下方式解決亂碼問(wèn)題:
方法一:手工調(diào)整瀏覽器的編碼
點(diǎn)擊鼠標(biāo)右鍵,在彈出菜單中選擇編碼,之后選擇UTF-8編碼。
方法二:使用html規(guī)范中的meta標(biāo)簽設(shè)置
添加上述代碼后,文字顯示正常。
方法三:使用Response對(duì)象的setHeader()方法設(shè)置Content-Type頭
另外,按照HTTP協(xié)議的規(guī)定,如果指定了消息正文的MIME類型后,瀏覽器就必須按照MIME類型解析,所以可以通過(guò)設(shè)置響應(yīng)消息頭Content-Type的值為text/html,并指定參數(shù)charset=UTF-8來(lái)使瀏覽器使用UTF-8解析頁(yè)面。
方法四:使用Response對(duì)象的setContentType()方法設(shè)置MIME類型及編碼
由于方法三的使用頻率比較高,所以在制定Servlet規(guī)范時(shí),JCP組織抽取了一個(gè)比較簡(jiǎn)單的方法,此方法與方法三的原理相同,只是更簡(jiǎn)單,在實(shí)際工作中,我們通常使用此方法指定MIME類型。
B.使用字符輸出流輸出響應(yīng):
Response對(duì)象的getWriter()方法可以返回一個(gè)PrintWriter對(duì)象,輸出的內(nèi)容會(huì)暫存在緩沖區(qū)中。當(dāng)響應(yīng)結(jié)束時(shí),Tomcat使用默認(rèn)編碼ISO-8859-1將Response對(duì)象的響應(yīng)消息正文轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)輸出給客戶端,而瀏覽器使用本地平臺(tái)默認(rèn)編碼進(jìn)行解碼,從而導(dǎo)致亂碼。
處理方式:使用response.setCharacterEncoding("UTF-8")方法告知Tomcat使用UTF-8而不是ISO-8859-1對(duì)響應(yīng)消息正文進(jìn)行編碼。另外,還需要使用response.setContentType("text/html;charset=UTF-8")告知瀏覽器使用UTF-8編碼解碼傳遞過(guò)來(lái)的數(shù)據(jù)。
修改后的顯示效果如下:
2.請(qǐng)求中的亂碼處理
在用戶提交表單時(shí),瀏覽器會(huì)按照當(dāng)前頁(yè)面的編碼設(shè)置對(duì)中文字符進(jìn)行編碼,并將內(nèi)容生成請(qǐng)求消息發(fā)送給服務(wù)器進(jìn)行解析。Tomcat服務(wù)器得到請(qǐng)求消息后,會(huì)依據(jù)表單數(shù)據(jù)的位置不同,做不同的處理。
當(dāng)提交方法(method)是POST時(shí),表單數(shù)據(jù)會(huì)置于請(qǐng)求消息正文中,Tomcat默認(rèn)使用ISO-8859-1對(duì)此部分內(nèi)容進(jìn)行解碼,此時(shí)需要使用Request.setCharacterEncoding("UTF-8")告知服務(wù)器使用UTF-8編碼對(duì)請(qǐng)求消息正文進(jìn)行解碼。
當(dāng)提交方法(method)是GET時(shí),表單數(shù)據(jù)會(huì)置于請(qǐng)求消息行中,并使用URL標(biāo)準(zhǔn)編碼方式進(jìn)行二次編碼。而Tomcat得到此部分?jǐn)?shù)據(jù)后,會(huì)先使用URL標(biāo)準(zhǔn)規(guī)范進(jìn)行解碼,然后默認(rèn)使用ISO-8859-1再進(jìn)行二次解碼。此時(shí)如果使用Request.getParamater()方法得到String字符串得到的是經(jīng)過(guò)ISO-8859-1解碼的字符串,故會(huì)發(fā)生亂碼問(wèn)題。對(duì)于此種情況,需要將得到的字符串重新按照ISO-8859-1打回字節(jié)數(shù)組,再重新解碼方可。
3.cookie中的亂碼處理;
因?yàn)閏ookie是分別使用Set-Cookie和Cookie消息頭在響應(yīng)和請(qǐng)求消息頭中進(jìn)行傳輸?shù)模鳫TTP協(xié)議中,響應(yīng)消息和請(qǐng)求消息中只能使用英文字符,中文字符是不安全字符無(wú)法直接使用。故在將中文存入Cookie中時(shí),需要進(jìn)行編碼操作。我們可以使用URLEncoder工具類的encode()方法對(duì)中文進(jìn)行編碼,在讀取時(shí)使用URLDecoder工具類的decode()方法進(jìn)行解碼。
使用URLEncoder.encode()方法對(duì)username字符串進(jìn)行編碼后再創(chuàng)建Cookie對(duì)象如下圖:
使用URLDecoder.decode()方法對(duì)username字符串進(jìn)行解碼如下圖:
三、 總結(jié)
以上的例子只是拋磚引玉,只要我們能抓住問(wèn)題的關(guān)鍵,了解亂碼的本質(zhì),那么亂碼什么的都是浮云。
以上就是關(guān)于JavaWeb中文編碼問(wèn)題方法的全部知識(shí)點(diǎn),感謝大家的學(xué)習(xí)和對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。