<kbd id="9plqc"><label id="9plqc"></label></kbd>

        <th id="9plqc"></th>
        1. <center id="9plqc"><video id="9plqc"></video></center>
          <sub id="9plqc"><form id="9plqc"><pre id="9plqc"></pre></form></sub>
          <nav id="9plqc"><form id="9plqc"><legend id="9plqc"></legend></form></nav>
          Web開發(fā)中常見亂碼處理 您所在的位置:網(wǎng)站首頁 屬馬幾月出生最好日歷網(wǎng)(wǎng) Web開發(fā)中常見亂碼處理

          Web開發(fā)中常見亂碼處理

          2024-04-10 01:18| 來源: 網(wǎng)絡(luò)整理| 查看: 265

          1. 字符編碼理論簡述 1.1 ASCII 1.2 ISO8859-1 1.3 Unicode 1.4 GBK 2. 可能發(fā)生的中文亂碼 2.1 中文變問號,如:??? 2.2 中文變奇怪字符,如:?? ?¥? 或者 ??o? 2.3 中文變“復(fù)雜中文”,如:浣犲ソ 2.4 中文變成一堆黑色菱形+問號,如:????? 3. Web開發(fā)中涉及到的中文編解碼 3.1 URL中出現(xiàn)的中文 3.2 Form表單中出現(xiàn)的中文 3.3 JSP中涉及的編碼 3.4 文件的上傳和下載中涉及到的中文亂碼 4. 總結(jié) 一、 字符編碼理論簡述

          本文主要是圍繞Web開發(fā)中涉及到的中文編碼這一常見問題展開,包括了對字符編碼基礎(chǔ)理論的簡述以及常見幾種編碼標(biāo)準(zhǔn)的介紹。其中包括:ASCII、ISO8859-1、Unicode、GBK。下面先對這些字符編碼集進(jìn)行簡單的介紹。 造成亂碼的原因為:在服務(wù)器端設(shè)置網(wǎng)頁的字符編碼方式與瀏覽器的解碼方式不同造成解碼沖突,導(dǎo)致出現(xiàn)亂碼

          CharSet字符集 字符集說明 ASCII (1字節(jié)) 英文字母和數(shù)字(都為一個字節(jié)) GBK (中2英1) 漢字編碼字符集 (中文字符2字節(jié),英文字符1個字節(jié)) UTF-8 (中3英1) 變長unicode字符(中文字符3字節(jié),英文字符1個字節(jié)) UTF-16BE ( 2字節(jié) ) 定長unicode字符(高字節(jié)低地址,統(tǒng)一為2字節(jié)) UTF-16LE ( 2字節(jié)) 定長unicode字符集,小端編碼(低字節(jié)低地址,統(tǒng)一為2字節(jié)) ISO-8859-1(1字節(jié)) 不能存儲漢字,定長為1字節(jié) 二、可能發(fā)生的中文亂碼

          這一小節(jié)介紹軟件開發(fā)中常見的中文編碼亂碼問題,在下面示例中:對于給定的一個包含中文的字符串"你好Java",看一下都會出現(xiàn)哪些亂碼問題。

          2.1 中文變問號,如:????? "你好Java" ------> "??Java"

          這種情況一般是由于中文字符經(jīng)ISO8859-1編碼造成的。下面是編碼的具體過程:

          原字符串:"你好Java"(默認(rèn))經(jīng)ISO8859-1編碼后:

          你 好 J a v a 3f 3f 4a 61 76 61

          上述字節(jié)碼再由UTF-8解碼后得:

          3f 3f 4a 61 76 61 ? ? J a v a

          解碼后字符串:"??Java"

          String str = "你好Java"; System.out.println(byteToHexString(str.getBytes(CHARSET_ISO88591))); System.out.println(new String(str.getBytes(CHARSET_ISO88591))); 輸出: 3f 3f 4a 61 76 61 ??Java

          我們知道ISO8859-1是單字節(jié)編碼,而對于漢字已經(jīng)超出ISO8859-1的編碼范圍,會被轉(zhuǎn)化為"3f",我們查UTF-8表可知,"3f"對應(yīng)的UTF-8字符正是"?"。

          亂碼原因:

          中文字符采用了ISO8859-1進(jìn)行編碼,然后經(jīng)過UTF-8解碼

          中文變問號的亂碼情況是非常常見的,大部分開源軟件的默認(rèn)編碼設(shè)置成了ISO8859-1,這點(diǎn)需要格外注意。

          2.2 中文變奇怪字符,如:?? ?¥? 或者 ??o? "你好Java" ------> "?? ?¥?Java"

          原字符串:"你好Java"經(jīng)UTF-8編碼后,一個中文用三個字節(jié)表示:

          你 好 J a v a e4 bd a0 e5 a5 bd 4a 61 76 61

          上述字節(jié)碼再由ISO8859-1解碼后得:

          e4 bd a0 e5 a5 bd 4a 61 76 61 ?? ?¥? J a v a

          亂碼原因:

          對中文字符先用UTF-8編碼或GBK編碼,再由ISO8859-1解碼,就會變成奇怪字符。對照ISO8859-1編碼表后發(fā)現(xiàn):e4 bd a0分別對應(yīng)三個字符:"?? ",e5 a5 bd分別對應(yīng)三個字符"?¥?",

          2.3 中文變“復(fù)雜中文”如:浣犲ソ

          下面依然是"你好Java"經(jīng)過UTF-8編碼后對應(yīng)的字節(jié)序列:

          你 好 J a v a e4 bd a0 e5 a5 bd 4a 61 76 61

          上述字節(jié)碼再由GBK解碼后得:

          e4 bd a0 e5 a5 bd 4a 61 76 61 浣 犲 ソ J a v a

          在GBK表中查找:e4 bd對應(yīng)字符:"浣",a0 e5對應(yīng)字符:"犲",a5 bd對應(yīng)字符:"ソ"

          亂碼原因:

          中文字符用UTF-8進(jìn)行編碼,然后用GBK解碼就得到了"復(fù)雜中文"

          同理,如果GBK編碼的中文用UTF-8來解碼的話,同樣會出現(xiàn)亂碼問題。

          2.4 中文變成一堆黑色菱形+問號,如:?????

          首先問號+黑色菱形的字符是Unicode中的"REPLACEMENT CHARACTER",該字符的主要作用是用來表示不識別的字符。 所以產(chǎn)生亂碼的原因可能有很多,下面通過原字符串:"你好Java",重現(xiàn)一種亂碼方式:

          原字符串:String str = "你好Java"經(jīng)過UTF-16BE編碼后對應(yīng)的字節(jié)序列:

          fe ff 4f 60 59 7d 0 4a 0 61 0 76 0 61

          其中"fe ff"就是字節(jié)流起始的BOM標(biāo)識符。"fe ff"在Unicode標(biāo)準(zhǔn)中屬于"noncharacters",只用于內(nèi)部使用。所以在使用UTF-8解碼輸出該字節(jié)序列的時候,沒有該碼元對應(yīng)的字符,對于不識別字符,就會用??替代。

          亂碼原因:

          中文字符用UTF-16BE進(jìn)行編碼,然后用UTF-8解碼

          三、Web開發(fā)中涉及到的中文編解碼

          Web中的數(shù)據(jù)大多通過http協(xié)議進(jìn)行傳輸,所涉及到的一些編解碼問題都圍繞著http協(xié)議。下面以Tomcat作為Web服務(wù)器, 探討下一個完整的請求響應(yīng)流程中哪些地方會涉及到中文的編解碼。

          3.1 url編解碼

          web環(huán)境中的中文亂碼問題,實驗如下:

          jsp中的form表單: 用戶名: 地址 后端使用SpringMVC的Controller: @Controller() @RequestMapping("/manager") public class ManagerController { @RequestMapping("/test/{param}") @ResponseBody public String test(@PathVariable String param, HttpServletRequest request){ String name = request.getParameter("name"); System.out.println("name:" + name + ",param:" + param); return "test"; } }

          表單中填入內(nèi)容: 用戶名:你好 Java 地址:123 提交請求,firebug中的顯示的url如下:

          http://localhost:8080/fdyuntu-ssm/manager/codec/%E4%BD%A0%E5%A5%BD

          查閱編碼可以,firefox對url中出現(xiàn)的中文使用了UTF-8的編碼方式。之所以url中出現(xiàn)%,這是因為根據(jù)URL編碼規(guī)范,瀏覽器會將非ASCII字符(ASCII中不包含中文字符)編成16進(jìn)制后,每個字節(jié)前需要加%。

          后端控制臺輸出:

          name:?? ?¥? Java,param:?? ?¥?

          可見無論是url中的中文信息或是post表單中的中文都出現(xiàn)了亂碼現(xiàn)象,從前一節(jié)中關(guān)于亂碼情況的分析來看,這里應(yīng)該是中文字符經(jīng)過瀏覽器UTF-8編碼后,Server端用ISO8859-1進(jìn)行解碼所致。下面逐個分析url和post表單如何進(jìn)行編解碼的。

          在tomcat中url的byte -> char的轉(zhuǎn)換是在org.apache.catalina.connector.CoyoteAdapter類的convertURI(MessageBytes uri, Request request)方法中執(zhí)行的,源碼如下:

          protected void convertURI(MessageBytes uri, Request request)throws Exception { ByteChunk bc = uri.getByteChunk(); int length = bc.getLength(); CharChunk cc = uri.getCharChunk(); cc.allocate(length, -1); //這里獲取的connector的URIEncoding屬性,即server.xml文件中connector元素的URIEncoding屬性 String enc = connector.getURIEncoding(); if (enc != null) { B2CConverter conv = request.getURIConverter(); try { if (conv == null) { conv = new B2CConverter(enc, true); request.setURIConverter(conv); } else { conv.recycle(); } } catch (IOException e) { log.error("Invalid URI encoding; using HTTP default"); connector.setURIEncoding(null); } if (conv != null) { try { conv.convert(bc, cc, true); uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength()); return; } catch (IOException ioe) { request.getResponse().sendError( HttpServletResponse.SC_BAD_REQUEST); } } } // 如果沒有配置URIEncoding,則在ByteChunk中默認(rèn)使用ISO8859-1。 byte[] bbuf = bc.getBuffer(); char[] cbuf = cc.getBuffer(); int start = bc.getStart(); for (int i = 0; i < length; i++) { cbuf[i] = (char) (bbuf[i + start] & 0xff); } uri.setChars(cbuf, 0, length); }

          在org.apache.tomcat.util.buf.ByteChunk中可以看到默認(rèn)編碼的定義:

          public final class ByteChunk implements Cloneable, Serializable { //。。。 public static final Charset DEFAULT_CHARSET = B2CConverter.ISO_8859_1; //。。。 }

          所以對于請求url中的中文,我們按UTF-8進(jìn)行編碼,在服務(wù)端卻按ISO8859-1進(jìn)行解碼,所以出現(xiàn)亂碼現(xiàn)象。我們可以在Tomcat的server.xml中指定url的編解碼格式,如下:

          此時重復(fù)上面實驗,后端控制臺輸出:name:?? ?¥? Java,param:你好

          雖然url中的參數(shù)可以正常顯示了,但是form表單中的參數(shù)name依然亂碼,下面進(jìn)一步分析。

          3.2 form表單元素的編解碼

          name參數(shù)的編碼依然是亂碼的,為啥?首先定位form表單中參數(shù)是在哪里進(jìn)行解碼的。Form表單中的字符解碼時機(jī)是發(fā)生在第一次調(diào)用request.getParameter時,可以通過request.setCharacterEncoding設(shè)置。需要注意的是setCharacterEncoding必須在getParameter之前調(diào)用!否則,setCharacterEncoding不會起作用。

          Tomcat中HttpServletRequest接口的實現(xiàn)類是org.apache.catalina.connector.Request。下面是Request類中g(shù)etParameter源碼:

          @Override public String getParameter(String name) { //判斷參數(shù)是否被解析過 if (!parametersParsed) { parseParameters();//第一次參數(shù)解析 } return coyoteRequest.getParameters().getParameter(name); } //下面是parseParameters部分源碼 protected void parseParameters() { //設(shè)為true,表示參數(shù)已解析過 parametersParsed = true; //Parameters對象封裝了form表單參數(shù) Parameters parameters = coyoteRequest.getParameters(); boolean success = false; try { // Set this every time in case limit has been changed via JMX parameters.setLimit(getConnector().getMaxParameterCount()); //獲取字符編碼格式 String enc = getCharacterEncoding(); boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); if (enc != null) { //getCharacterEncoding不為null,則對應(yīng)設(shè)置編碼方式 parameters.setEncoding(enc); if (useBodyEncodingForURI) { parameters.setQueryStringEncoding(enc); } } else { //如果enc為null,則編碼方式設(shè)置為DEFAULT_CHARACTER_ENCODING,也就是ISO8859-1 parameters.setEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); if (useBodyEncodingForURI) { parameters.setQueryStringEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); } } parameters.handleQueryParameters(); 。。。 } }

          從以上源碼中可以看出為什么需要在第一次調(diào)用getParameter之前設(shè)置CharacterEncoding。因為第一次執(zhí)行parseParameters時,會把parametersParsed變量設(shè)為true。所以parseParameters只會在第一次getParameter時調(diào)用。有時會出現(xiàn)這么一種怪像:通過request.getCharacterEncoding()得到的是我們認(rèn)為正確的編碼字符集,但是request.getParameter得到的依然是亂碼。此時就需要考慮下我們調(diào)用setCharacterEncoding之前是否已經(jīng)調(diào)用過getParameter方法了。

          經(jīng)過上面的分析后,對于form表單參數(shù)亂碼問題就很好解決了,在第一次調(diào)用request.getParameter方法前,通過request.setCharacterEncoding("Expected_Encoding");設(shè)置即可。這一步可以用Servlet標(biāo)準(zhǔn)中的Filter實現(xiàn),不過,常用的MVC框架中已經(jīng)有現(xiàn)成的Filter實現(xiàn)了,比如SpringMVC中的org.springframework.web.filter.CharacterEncodingFilter,如下:

          @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) { request.setCharacterEncoding(this.encoding);//設(shè)置指定的編碼 if (this.forceEncoding) { response.setCharacterEncoding(this.encoding); } } filterChain.doFilter(request, response); } 3.3 JSP中涉及的編碼

          jsp中可以通過page指令指定一些編碼參數(shù),如下:

          pageEncoding="UTF-8"在什么時候起作用?

          在Servlet標(biāo)準(zhǔn)中,jsp最終也會被編譯成一個servlet。index.jsp->index_jsp.java.pageEncoding="UTF-8"就是在這個解析過程中起作用的。

          contentType="text/html; charset=UTF-8"的作用?

          contentType是響應(yīng)頭中特定信息,主要的作用是告訴瀏覽器response中存放的主體對象類型和編碼,這樣瀏覽器就可以對指定類型進(jìn)行正確解碼,保證了數(shù)據(jù)在server和client端的一致性。當(dāng)進(jìn)行Servlet編程的時候,可以手動進(jìn)行設(shè)置,如下:

          response.setContentType("text/html; charset=UTF-8"); 3.4 文件的上傳和下載中涉及到的中文亂碼

          Web中的文件作主要是上傳和下載,這個過程也是依托于Http協(xié)議作為數(shù)據(jù)載體。所以,最終是否亂碼重點(diǎn)在于是否正確的設(shè)置http的request、response的header中的相關(guān)字段。如ContentType、Content-Disposition的設(shè)定等。如下:

          response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("application/x-msdownload"); response.addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

          這里需要注意的是Content-Disposition的filename屬性值,如果fileName含有中文,那么要格外注意fileName字符串的編碼格式。在rfc5987對于HTTP的Header中參數(shù)的編碼做出了明確的規(guī)定:

          By default, message header field parameters in Hypertext Transfer Protocol (HTTP) messages cannot carry characters outside the ISO-8859-1 character set.

          也就是說默認(rèn)情況下,Http的Header中的參數(shù)只能用ISO-8859-1字符集中的字符,那么是否意味著Content-Disposition中的fileName字符串也要轉(zhuǎn)成ISO-8859-1了呢?答案是:NO!原因如下:Content-Disposition其實不屬于Http/1.1標(biāo)準(zhǔn)。這在RFC2616中有明確的說明。只因為其使用廣泛,HTTP才對其支持。在rfc6266中也詳細(xì)介紹了Content-Disposition的filename參數(shù)含義和用法。下面是對于下載包含中文名稱的文件時的解決方案。

          解決方案

          最簡單就是直接用ISO8859-1對文件名進(jìn)行編碼,大多數(shù)瀏覽器都支持。如下:

          exportFileName.getBytes("UTF-8"),"ISO8859-1");//這里的UTF-8也可能是別的編碼,主要依據(jù)系統(tǒng)默認(rèn)的編碼來設(shè)定。

          或通過其它編碼,如UTF-8。

          response.addHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(exportFileName, "UTF8")); 四、總結(jié)

          編解碼問題是多語言交互系統(tǒng)中必然要面對的問題,尤其對于中文環(huán)境中的開發(fā)者來說,在入門階段或多或少都會遇到此類問題。亂碼問題本質(zhì)就是通信雙方使用的標(biāo)準(zhǔn)不一致。所以,解決亂碼問題的方法其實也很簡單,統(tǒng)一下編解碼標(biāo)準(zhǔn)即可。此外,深入理解各種編碼標(biāo)準(zhǔn)的原理和關(guān)系也非常重要,在以后遇到類似問題的時候能夠更加準(zhǔn)確的判斷出造成亂碼的原因。



          【本文地址】

          公司簡介

          聯(lián)系我們

          今日新聞

          推薦新聞

          專題文章
            CopyRight 2018-2019 實驗室設(shè)備網(wǎng) 版權(quán)所有
            黄色免费网站在线看,韩国精品在线观看,韩国美女一区二区,99国产热 襄城县| 雅江县| 临泽县| 淮安市| 霸州市| 汉川市| 六枝特区| 尼玛县| 青铜峡市| 化德县| 永康市| 松江区| 海淀区| 西安市| 无为县| 多伦县| 余庆县| 海兴县| 内乡县| 吐鲁番市| 定边县| 庆元县| 青铜峡市| 榆树市| 增城市| 吴旗县| 开鲁县| 如皋市| 南岸区| 永平县| 印江| 万全县| 怀安县| 通州区| 洛浦县| 汉寿县| 东海县| 军事| 赫章县| 海宁市| 双桥区| http://444 http://444 http://444 http://444 http://444 http://444