backup2007

          導(dǎo)航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          統(tǒng)計

          公告

          @import url(http://www.aygfsteel.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);


          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          編碼問題1 轉(zhuǎn)

          HTML:
              ……
              //實際上這里的charset=utf-8 也是可以的,因為在中文平臺下
              //用了GB2312
             

          JS:
              我用了兩個方法提交:GET 和 POST。
              在服務(wù)器端要對應(yīng)不同的提交方式轉(zhuǎn)換不同的編碼。

             ……
              //要傳遞的參數(shù)
             var queryString = "firstName=" + firstName + "&lastName=" + lastName
                                    + "&birthday=" + birthday;    function  
           
              //GET方式提交
              doRequestUsingGET() {
                  createXMLHttpRequest();
                  var url = "GetAndPostExample?" + queryString + "&timeStamp="
                                    + new Date().getTime();
                  xmlHttp.onreadystatechange = handleStateChange;
                  xmlHttp.open("GET", url, true);
                  xmlHttp.send(null);
              }

              //POST方式提交
              function doRequestUsingPOST() {
                  createXMLHttpRequest();
                  var url = "GetAndPostExample?timeStamp=" + new Date().getTime();
                  xmlHttp.open("POST", url, true);
                  xmlHttp.onreadystatechange = handleStateChange;
                  xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                  xmlHttp.send(queryString);
              }

          servlet:
                  response.setContentType("text/xml");

                  //這個一定要設(shè)置,這里的設(shè)置應(yīng)該跟HTML中的一樣,但是我在這里
                  //用了 uft-8, 結(jié)果也是一樣。
                  response.setCharacterEncoding("GB2312");
                
                  //當(dāng)用POST方法時,一定要設(shè)置成utf-8,否則亂碼
                  String firstName = new String(request.getParameter("firstName").getBytes("ISO-8859-1"), "utf-8");

                  //當(dāng)用GET方法時,要設(shè)置成GB2312,否則亂碼。
                  String lastName = new String(request.getParameter("lastName").getBytes("ISO-8859-1"), "GB2312");

          測試結(jié)果:
          因為界面上兩個控件firstName 和  lastName都輸入中文。
          接收xmlHttp.responseText后,會發(fā)現(xiàn)其中一個為亂碼,一個可以正常顯示中文。

          -----------------------------------------------------------------------------------------------------------

          這里的方案是頁面(page)是基于GBK(gb2312) 的編碼格式

          AJAX的中文情況,默認(rèn)情況下,tomcat和weblogic的get和post請求的編碼方案都不一樣,不過前提都是要設(shè)置 request.setCharactorEncoding("UTF-8")如下面代碼中,get方案是很煩瑣的那種將得到的參數(shù)重新編碼來實現(xiàn)的 |new String(request.getParameter("para name").getBytes("encoding name"))|而post方案是比較簡單而方便的,也提倡使用這種方式,因為可以基于filter來管理編碼
          tomcat可以通過設(shè)置在 server.xml里的Connector元素下設(shè)置URIencoding="gbk"參數(shù)來讓get使用post的方案(即get和post都使用 request.setCharactorEncoding("UTF-8"),request.getParameter("para name") )具體設(shè)置參考http://www.javaeye.com/topic/131542,不過在weblogic下無解(我個人還沒發(fā)現(xiàn)如何實現(xiàn)), weblogic好像在解析get參數(shù)后自己又用什么編碼格式包裝過......其實AJAX get根本沒有普通請求get請求作為標(biāo)簽的作用,我們完全可以不使用get,而只使用post

          測試用的jsp和servlet在下面,丟到一個項目里,在web.xml里配置servlet后運(yùn)行可以看到效果,servlet的url- pattern是這個<url-pattern>/GetAndPostExample</url-pattern>

          getAndPostExample.jsp

          代碼
          1. <%@ page language="java" import="java.util.Date"  contentType="text/html; charset=gbk"%>  
          2. <html>  
          3.     <head>  
          4.         <title>發(fā)送帶參數(shù)的信息到服務(wù)器,以及get,post的區(qū)別</title>  
          5.         <script type="text/javascript">  
          6.             var xmlHttp;               
          7.             function show()   
          8.                 {   
          9.                 document.getElementById("show").value=document.getElementById("firstName").value;   
          10.                 }   
          11.             function createXMLHttpRequest()   
          12.                 {   
          13.                 if (window.ActiveXObject)   
          14.                     xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");   
          15.                 else if (window.XMLHttpRequest)   
          16.                     xmlHttp = new XMLhttpRequest();   
          17.                 }   
          18.                
          19.             function createQueryString()   
          20.                 {   
          21.                 var firstName = document.getElementById("firstName").value;   
          22.                 var secondName = document.getElementById("secondName").value;   
          23.                 var birthday = document.getElementById("birthday").value;   
          24.                    
          25.                 var queryString = "firstName="+firstName+"&secondName="+secondName   
          26.                                                 +"&birthday="+birthday;   
          27.                 return queryString;   
          28.                 }   
          29.                
          30.             function doRequestUsingGET()   
          31.                 {   
          32.                 createXMLHttpRequest();   
          33.                 show();   
          34.                 var queryString = "GetAndPostExample?";   
          35.                 queryStringqueryString = queryString + createQueryString()   
          36.                                         + "&timeStamep=" + new Date().getTime();   
          37.                 xmlHttp.onreadystatechange = handleStateChange;   
          38.                 xmlHttp.open("GET",queryString,true);   
          39.                                     xmlHttp.setRequestHeader("RequestType","ajax");   
          40.                 xmlHttp.send(null);   
          41.                 //alert(queryString);   
          42.                 }   
          43.                    
          44.             function doRequestUsingPOST()   
          45.                 {   
          46.                 createXMLHttpRequest();   
          47.                 show();        
          48.                 var url = "GetAndPostExample"  
          49.                 var queryString = createQueryString()+ "&timeStamp="+ new Date().getTime();            
          50.                 xmlHttp.open("POST",url,true);   
          51.                 xmlHttp.onreadystatechange = handleStateChange;   
          52.                 xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");   
          53.                                      //設(shè)置報頭,說明此請求是ajax請求   
          54.                                      xmlHttp.setRequestHeader("RequestType","ajax");   
          55.                 xmlHttp.send(queryString);   
          56.                 }   
          57.                    
          58.             function handleStateChange()   
          59.                 {   
          60.                 if (xmlHttp.readyState == 4)   
          61.                     {   
          62.                     if (xmlHttp.status == 200)   
          63.                         parseResults();   
          64.                     }   
          65.                 }   
          66.                
          67.             function parseResults()   
          68.                 {   
          69.                 var responseDiv = document.getElementById("serverResponse");   
          70.                 if (responseDiv.hasChildNodes())   
          71.                     {   
          72.                     responseDiv.removeChild(responseDiv.childNodes[0]);   
          73.                     }   
          74.                 var responseText = document.createTextNode(xmlHttp.responseText);   
          75.                 responseDiv.appendChild(responseText);   
          76.                 }   
          77.                
          78.         </script>  
          79.     </head>  
          80.     <body>  
          81.         <h1>輸入你的姓,名,生日日期</h1>  
          82.         <br>  
          83.            
          84.             <table>  
          85.                 <tr>  
          86.                     <td>  
          87.                         姓:   
          88.                     </td>  
          89.                     <td>  
          90.                         <input type="text" name="firstName" id="firstName" value="羽飛">  
          91.                     </td>  
          92.                 </tr>  
          93.                     <td>  
          94.                         名:   
          95.                     </td>  
          96.                     <td>  
          97.                         <input type="text" name="secondName" id="secondName" value="翼">  
          98.                     </td>  
          99.                 <tr>  
          100.                 </tr>  
          101.                 <tr>  
          102.                     <td>  
          103.                         生日:   
          104.                     </td>  
          105.                     <td>  
          106.                         <input type="text" name="birthday" id="birthday" value="五月">  
          107.                     </td>  
          108.                     <td>  
          109.                         <input type="text" name="show" id="show">  
          110.                     </td>  
          111.                 </tr>  
          112.             </table>  
          113.         <form action="#">  
          114.             <input type="button" value="使用GET提交" onclick="doRequestUsingGET();">  
          115.             <br>  
          116.             <input type="button" value="使用POST提交" onclick="doRequestUsingPOST();">  
          117.         </form>  
          118.         <br>  
          119.         <br>           
          120.         <h2>服務(wù)器返回信息:</h2>          
          121.         <div id="serverResponse">  
          122.         </div>  
          123.     </body>  
          124. </html>  

          GetAndPostExample.java

          代碼
          1. package yufei;   
          2.   
          3. import java.io.*;   
          4. import java.net.*;   
          5. import javax.servlet.*;   
          6. import javax.servlet.http.*;   
          7.   
          8. public class GetAndPostExample extends HttpServlet   
          9.     {   
          10.         protected void processRequest(HttpServletRequest request,   
          11.                 HttpServletResponse response, String method1)   
          12.                 throws ServletException,IOException   
          13.                 {   
          14.                 //設(shè)置文本類型(content type)   
          15.                 response.setContentType("text/xml");   
          16.                                      //設(shè)置文本類型的編碼格式   
          17.                 response.setCharacterEncoding("GBK");   
          18.                 response.setHeader("Cache-Control","no-cache");   
          19.                    
          20.                 String firstName =null;   
          21.                 String secondName = null;   
          22.                 String birthday = null;   
          23.                                     //無論是get還是post,都要使用下面這句   
          24.                                     request.setCharacterEncoding("UTF-8");   
          25.                 if (method1.equals("GET"))   
          26.                 {                   
          27.                  firstName = new String(request.getParameter("firstName").getBytes("ISO8859-1"));   
          28.                  secondName = new String(request.getParameter("secondName").getBytes("ISO8859-1"));   
          29.                  birthday = new String(request.getParameter("birthday").getBytes("ISO8859-1"));   
          30.                 }   
          31.                 else if (method1.equals("POST"))   
          32.                 {                            firstName = request.getParameter("firstName");   
          33.                  secondName = request.getParameter("secondName");   
          34.                  birthday = request.getParameter("birthday");   
          35.                 }      
          36.                 String responseText = "Hello " + firstName + " " + secondName   
          37.                                 + " 你的生日是 " + birthday + " " + "(method: " + method1 + ")";                
          38.                 PrintWriter out = response.getWriter();   
          39.                 out.println(responseText);   
          40.                    
          41.                 out.close();   
          42.                 }   
          43.         protected void doGet(HttpServletRequest request,   
          44.                 HttpServletResponse response)   
          45.                 throws ServletException,IOException   
          46.                 {   
          47.                 processRequest(request,response,"GET");   
          48.                 }   
          49.         protected void doPost(HttpServletRequest request,   
          50.                 HttpServletResponse response)   
          51.                 throws ServletException,IOException   
          52.                 {   
          53.                 processRequest(request,response,"POST");   
          54.                 }   
          55.     }   

          -
          -

          當(dāng)我們的ajax請求只使用post(tomcat下可以實現(xiàn)get和post同樣方案)請求時,我們可以使用過濾器來實現(xiàn)其編碼設(shè)置,就可以把 servlet中的request.setCharactorEncoding提出來,去掉servlet里的 request.setCharactorEncoding("encoding name"),加入下面的過濾器
          根據(jù)fins大大的指導(dǎo),將過濾器重寫為可以區(qū)分普通請求和ajax請求的樣式了(ajax請求中設(shè)置了header)

          SetCharacterEncodingFilter.java

          代碼
          1. package yufei;   
          2.   
          3. import java.io.IOException;   
          4. import javax.servlet.FilterChain;   
          5. import javax.servlet.FilterConfig;   
          6. import javax.servlet.ServletException;   
          7. import javax.servlet.ServletRequest;   
          8. import javax.servlet.ServletResponse;   
          9. import javax.servlet.Filter;   
          10. import javax.servlet.http.HttpServletRequest;   
          11.   
          12. public class CharactorEncodingFilter implements Filter   
          13.     {   
          14.     public CharactorEncodingFilter()   
          15.         {   
          16.         super();   
          17.         }   
          18.     private FilterConfig filterConfig;   
          19.     private String ajaxEncoding = "UTF-8";   
          20.     private String commonEncoding;   
          21.     protected boolean ignore = true;   
          22.     public void init(FilterConfig filterConfig) throws ServletException   
          23.         {   
          24.         this.filterConfig = filterConfig;   
          25.         commonEncoding = filterConfig.getInitParameter("CommonRequestEncoding");   
          26.         String value = filterConfig.getInitParameter("ignore");   
          27.         if (value == null)   
          28.             this.ignore = true;   
          29.         else if (value.equalsIgnoreCase("true"))   
          30.             this.ignore = true;   
          31.         else if (value.equalsIgnoreCase("yes"))   
          32.             this.ignore = true;   
          33.         else  
          34.             this.ignore = false;   
          35.         }   
          36.   
          37.     public void doFilter(ServletRequest req, ServletResponse res,   
          38.         FilterChain filterChain)   
          39.   
          40.         {   
          41.         try  
          42.             {   
          43.             HttpServletRequest request = (HttpServletRequest) req;   
          44.             if (ignore || (request.getCharacterEncoding() == null))   
          45.                 {   
          46.                 if (request.getHeader("RequestType") != null  
          47.                     && request.getHeader("RequestType")   
          48.                         .equalsIgnoreCase("ajax"))   
          49.                     {   
          50.                     request.setCharacterEncoding(ajaxEncoding);   
          51.                     }   
          52.                 else if (commonEncoding != null)   
          53.                     {   
          54.                     request.setCharacterEncoding(commonEncoding);   
          55.                     }   
          56.                 else  
          57.                     {   
          58.                     request.setCharacterEncoding("UTF-8");   
          59.                     }   
          60.                 }   
          61.             filterChain.doFilter(req, res);   
          62.             }   
          63.         catch (IOException e)   
          64.             {   
          65.             e.printStackTrace();   
          66.             }   
          67.         catch (ServletException e)   
          68.             {   
          69.             e.printStackTrace();   
          70.             }   
          71.         }   
          72.     public void destroy()   
          73.         {   
          74.         this.commonEncoding = null;   
          75.         this.filterConfig = null;   
          76.         }   
          77.     }   

          web.xml加入如下過濾器配置

          代碼
          1. <filter>  
          2.     <filter-name>CharactorEncoding</filter-name>  
          3.     <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>  
          4.                  <!-- 設(shè)置編碼格式到initparameter里去 -->  
          5.     <init-param>  
          6.         <param-name>CommonRequestEncoding</param-name>  
          7.         <param-value>GBK</param-value>  
          8.     </init-param>          
          9. </filter>  
          10. <filter-mapping>  
          11.     <filter-name>CharactorEncoding</filter-name>  
          12.     <url-pattern>/*</url-pattern>  
          13. </filter-mapping>  
          14. ---------------------------------------------------------------------------------------------------
          15. 字符,字節(jié)和編碼

            [原創(chuàng)文章,轉(zhuǎn)載請保留或注明出處:http://www.regexlab.com/zh/encoding.htm]

            級別:中級

            摘要:本文介紹了字符與編碼的發(fā)展過程,相關(guān)概念的正確理解。舉例說明了一些實際應(yīng)用中,編碼的實現(xiàn)方法。然后,本文講述了通常對字符與編碼的幾種誤解,由于這些誤解而導(dǎo)致亂碼產(chǎn)生的原因,以及消除亂碼的辦法。本文的內(nèi)容涵蓋了“中文問題”,“亂碼問題”。

            掌握編碼問題的關(guān)鍵是正確地理解相關(guān)概念,編碼所涉及的技術(shù)其實是很簡單的。因此,閱讀本文時需要慢讀多想,多思考。

            引言

            “字符與編碼”是一個被經(jīng)常討論的話題。即使這樣,時常出現(xiàn)的亂碼仍然困擾著大家。雖然我們有很多的辦法可以用來消除亂碼,但我們并不一定理解這些 辦法的內(nèi)在原理。而有的亂碼產(chǎn)生的原因,實際上由于底層代碼本身有問題所導(dǎo)致的。因此,不僅是初學(xué)者會對字符編碼感到模糊,有的底層開發(fā)人員同樣對字符編 碼缺乏準(zhǔn)確的理解。

            回頁首

            1. 編碼問題的由來,相關(guān)概念的理解

            1.1 字符與編碼的發(fā)展

            從計算機(jī)對多國語言的支持角度看,大致可以分為三個階段:

              系統(tǒng)內(nèi)碼 說明 系統(tǒng)
            階段一 ASCII 計算機(jī)剛開始只支持英語,其它語言不能夠在計算機(jī)上存儲和顯示。 英文 DOS
            階段二 ANSI編碼
            (本地化)
            為使計算機(jī)支持更多語言,通常使用 0x80~0xFF 范圍的 2 個字節(jié)來表示 1 個字符。比如:漢字 '中' 在中文操作系統(tǒng)中,使用 [0xD6,0xD0] 這兩個字節(jié)存儲。

            不同的國家和地區(qū)制定了不同的標(biāo)準(zhǔn),由此產(chǎn)生了 GB2312, BIG5, JIS 等各自的編碼標(biāo)準(zhǔn)。這些使用 2 個字節(jié)來代表一個字符的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文系統(tǒng)下,ANSI 編碼代表 GB2312 編碼,在日文操作系統(tǒng)下,ANSI 編碼代表 JIS 編碼。

            不同 ANSI 編碼之間互不兼容,當(dāng)信息在國際間交流時,無法將屬于兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。
            中文 DOS,中文 Windows 95/98,日文 Windows 95/98
            階段三 UNICODE
            (國際化)
            為了使國際間信息交流更加方便,國際組織制定了 UNICODE 字符集,為各種語言中的每一個字符設(shè)定了統(tǒng)一并且唯一的數(shù)字編號,以滿足跨語言、跨平臺進(jìn)行文本轉(zhuǎn)換、處理的要求。 Windows NT/2000/XP,Linux,Java

            字符串在內(nèi)存中的存放方法:

            在 ASCII 階段,單字節(jié)字符串使用一個字節(jié)存放一個字符(SBCS)。比如,"Bob123" 在內(nèi)存中為:

            42 6F 62 31 32 33 00
            B o b 1 2 3 \0

            在使用 ANSI 編碼支持多種語言階段,每個字符使用一個字節(jié)或多個字節(jié)來表示(MBCS),因此,這種方式存放的字符也被稱作多字節(jié)字符。比如,"中文123" 在中文 Windows 95 內(nèi)存中為7個字節(jié),每個漢字占2個字節(jié),每個英文和數(shù)字字符占1個字節(jié):

            D6 D0 CE C4 31 32 33 00
            1 2 3 \0

            在 UNICODE 被采用之后,計算機(jī)存放字符串時,改為存放每個字符在 UNICODE 字符集中的序號。目前計算機(jī)一般使用 2 個字節(jié)(16 位)來存放一個序號(DBCS),因此,這種方式存放的字符也被稱作寬字節(jié)字符。比如,字符串 "中文123" 在 Windows 2000 下,內(nèi)存中實際存放的是 5 個序號:

            2D 4E 87 65 31 00 32 00 33 00 00 00      ← 在 x86 CPU 中,低字節(jié)在前
            1 2 3 \0  

            一共占 10 個字節(jié)。

            回頁首

            1.2 字符,字節(jié),字符串

            理解編碼的關(guān)鍵,是要把字符的概念和字節(jié)的概念理解準(zhǔn)確。這兩個概念容易混淆,我們在此做一下區(qū)分:

              概念描述 舉例
            字符 人們使用的記號,抽象意義上的一個符號。 '1', '中', 'a', '$', '¥', ……
            字節(jié) 計算機(jī)中存儲數(shù)據(jù)的單元,一個8位的二進(jìn)制數(shù),是一個很具體的存儲空間。 0x01, 0x45, 0xFA, ……
            ANSI
            字符串
            在內(nèi)存中,如果“字符”是以 ANSI 編碼形式存在的,一個字符可能使用一個字節(jié)或多個字節(jié)來表示,那么我們稱這種字符串為 ANSI 字符串或者多字節(jié)字符串 "中文123"
            (占7字節(jié))
            UNICODE
            字符串
            在內(nèi)存中,如果“字符”是以在 UNICODE 中的序號存在的,那么我們稱這種字符串為 UNICODE 字符串或者寬字節(jié)字符串 L"中文123"
            (占10字節(jié))

            由于不同 ANSI 編碼所規(guī)定的標(biāo)準(zhǔn)是不相同的,因此,對于一個給定的多字節(jié)字符串,我們必須知道它采用的是哪一種編碼規(guī)則,才能夠知道它包含了哪些“字符”。而對于 UNICODE 字符串來說,不管在什么環(huán)境下,它所代表的“字符”內(nèi)容總是不變的。

            回頁首

            1.3 字符集與編碼

            各個國家和地區(qū)所制定的不同 ANSI 編碼標(biāo)準(zhǔn)中,都只規(guī)定了各自語言所需的“字符”。比如:漢字標(biāo)準(zhǔn)(GB2312)中沒有規(guī)定韓國語字符怎樣存儲。這些 ANSI 編碼標(biāo)準(zhǔn)所規(guī)定的內(nèi)容包含兩層含義:

            1. 使用哪些字符。也就是說哪些漢字,字母和符號會被收入標(biāo)準(zhǔn)中。所包含“字符”的集合就叫做“字符集”。
            2. 規(guī)定每個“字符”分別用一個字節(jié)還是多個字節(jié)存儲,用哪些字節(jié)來存儲,這個規(guī)定就叫做“編碼”。

            各個國家和地區(qū)在制定編碼標(biāo)準(zhǔn)的時候,“字符的集合”和“編碼”一般都是同時制定的。因此,平常我們所說的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”這層含義外,同時也包含了“編碼”的含義。

            UNICODE 字符集”包含了各種語言中使用到的所有“字符”。用來給 UNICODE 字符集編碼的標(biāo)準(zhǔn)有很多種,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。

            回頁首

            1.4 常用的編碼簡介

            簡單介紹一下常用的編碼規(guī)則,為后邊的章節(jié)做一個準(zhǔn)備。在這里,我們根據(jù)編碼規(guī)則的特點(diǎn),把所有的編碼分成三類:

            分類 編碼標(biāo)準(zhǔn) 說明
            單字節(jié)字符編碼 ISO-8859-1 最簡單的編碼規(guī)則,每一個字節(jié)直接作為一個 UNICODE 字符。比如,[0xD6, 0xD0] 這兩個字節(jié),通過 iso-8859-1 轉(zhuǎn)化為字符串時,將直接得到 [0x00D6, 0x00D0] 兩個 UNICODE 字符,即 "ÖÐ"。

            反之,將 UNICODE 字符串通過 iso-8859-1 轉(zhuǎn)化為字節(jié)串時,只能正常轉(zhuǎn)化 0~255 范圍的字符。
            ANSI 編碼 GB2312,
            BIG5,
            Shift_JIS,
            ISO-8859-2 ……
            把 UNICODE 字符串通過 ANSI 編碼轉(zhuǎn)化為“字節(jié)串”時,根據(jù)各自編碼的規(guī)定,一個 UNICODE 字符可能轉(zhuǎn)化成一個字節(jié)或多個字節(jié)。

            反之,將字節(jié)串轉(zhuǎn)化成字符串時,也可能多個字節(jié)轉(zhuǎn)化成一個字符。比如,[0xD6, 0xD0] 這兩個字節(jié),通過 GB2312 轉(zhuǎn)化為字符串時,將得到 [0x4E2D] 一個字符,即 '中' 字。

            “ANSI 編碼”的特點(diǎn):
            1. 這些“ANSI 編碼標(biāo)準(zhǔn)”都只能處理各自語言范圍之內(nèi)的 UNICODE 字符。
            2. “UNICODE 字符”與“轉(zhuǎn)換出來的字節(jié)”之間的關(guān)系是人為規(guī)定的。
            UNICODE 編碼 UTF-8,
            UTF-16, UnicodeBig ……
            與“ANSI 編碼”類似的,把字符串通過 UNICODE 編碼轉(zhuǎn)化成“字節(jié)串”時,一個 UNICODE 字符可能轉(zhuǎn)化成一個字節(jié)或多個字節(jié)。

            與“ANSI 編碼”不同的是:
            1. 這些“UNICODE 編碼”能夠處理所有的 UNICODE 字符。
            2. “UNICODE 字符”與“轉(zhuǎn)換出來的字節(jié)”之間是可以通過計算得到的。

            我們實際上沒有必要去深究每一種編碼具體把某一個字符編碼成了哪幾個字節(jié),我們只需要知道“編碼”的概念就是把“字符”轉(zhuǎn)化成“字節(jié)”就可以了。對 于“UNICODE 編碼”,由于它們是可以通過計算得到的,因此,在特殊的場合,我們可以去了解某一種“UNICODE 編碼”是怎樣的規(guī)則。

            回頁首

            2. 字符與編碼在程序中的實現(xiàn)

            2.1 程序中的字符與字節(jié)

            在 C++ 和 Java 中,用來代表“字符”和“字節(jié)”的數(shù)據(jù)類型,以及進(jìn)行編碼的方法:

            類型或操作 C++ Java
            字符 wchar_t char
            字節(jié) char byte
            ANSI 字符串 char[] byte[]
            UNICODE 字符串 wchar_t[] String
            字節(jié)串→字符串 mbstowcs(), MultiByteToWideChar() string = new String(bytes, "encoding")
            字符串→字節(jié)串 wcstombs(), WideCharToMultiByte() bytes = string.getBytes("encoding")

            以上需要注意幾點(diǎn):

            1. Java 中的 char 代表一個“UNICODE 字符(寬字節(jié)字符)”,而 C++ 中的 char 代表一個字節(jié)。
            2. MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函數(shù)。

            回頁首

            2.2 C++ 中相關(guān)實現(xiàn)方法

            聲明一段字符串常量:

            // ANSI 字符串,內(nèi)容長度 7 字節(jié)
            char
                 sz[20] = "中文123";

            // UNICODE 字符串,內(nèi)容長度 5 個 wchar_t(10 字節(jié))
            wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";

            UNICODE 字符串的 I/O 操作,字符與字節(jié)的轉(zhuǎn)換操作:

            // 運(yùn)行時設(shè)定當(dāng)前 ANSI 編碼,VC 格式
            setlocale(LC_ALL, ".936");

            // GCC 中格式
            setlocale(LC_ALL, "zh_CN.GBK");

            // Visual C++ 中使用小寫 %s,按照 setlocale 指定編碼輸出到文件
            // GCC 中使用大寫 %S

            fwprintf(fp, L"%s\n", wsz);

            // 把 UNICODE 字符串按照 setlocale 指定的編碼轉(zhuǎn)換成字節(jié)
            wcstombs(sz, wsz, 20);
            // 把字節(jié)串按照 setlocale 指定的編碼轉(zhuǎn)換成 UNICODE 字符串
            mbstowcs(wsz, sz, 20);

            在 Visual C++ 中,UNICODE 字符串常量有更簡單的表示方法。如果源程序的編碼與當(dāng)前默認(rèn) ANSI 編碼不符,則需要使用 #pragma setlocale,告訴編譯器源程序使用的編碼:

            // 如果源程序的編碼與當(dāng)前默認(rèn) ANSI 編碼不一致,
            // 則需要此行,編譯時用來指明當(dāng)前源程序使用的編碼

            #pragma setlocale
            (".936")

            // UNICODE 字符串常量,內(nèi)容長度 10 字節(jié)
            wchar_t wsz[20] = L"中文123";

            以上需要注意 #pragma setlocale 與 setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在編譯時起作用,setlocale() 在運(yùn)行時起作用。

            回頁首

            2.3 Java 中相關(guān)實現(xiàn)方法

            字符串類 String 中的內(nèi)容是 UNICODE 字符串:

            // Java 代碼,直接寫中文
            String
            string = "中文123";

            // 得到長度為 5,因為是 5 個字符
            System.out.println(string.length());

            字符串 I/O 操作,字符與字節(jié)轉(zhuǎn)換操作。在 Java 包 java.io.* 中,以“Stream”結(jié)尾的類一般是用來操作“字節(jié)串”的類,以“Reader”,“Writer”結(jié)尾的類一般是用來操作“字符串”的類。

            // 字符串與字節(jié)串間相互轉(zhuǎn)化

            // 按照 GB2312 得到字節(jié)(得到多字節(jié)字符串)

            byte
            [] bytes = string.getBytes("GB2312");

            // 從字節(jié)按照 GB2312 得到 UNICODE 字符串
            string = new String(bytes, "GB2312");

            // 要將 String 按照某種編碼寫入文本文件,有兩種方法:

            // 第一種辦法:用 Stream 類寫入已經(jīng)按照指定編碼轉(zhuǎn)化好的字節(jié)串

            OutputStream os = new FileOutputStream("1.txt");
            os.write(bytes);
            os.close();

            // 第二種辦法:構(gòu)造指定編碼的 Writer 來寫入字符串
            Writer ow = new OutputStreamWriter(new FileOutputStream("2.txt"), "GB2312");
            ow.write(string);
            ow.close();

            /* 最后得到的 1.txt 和 2.txt 都是 7 個字節(jié) */

            如果 java 的源程序編碼與當(dāng)前默認(rèn) ANSI 編碼不符,則在編譯的時候,需要指明一下源程序的編碼。比如:

            E:\>javac -encoding BIG5 Hello.java

            以上需要注意區(qū)分源程序的編碼與 I/O 操作的編碼,前者是在編譯時起作用,后者是在運(yùn)行時起作用。

            回頁首

            3. 幾種誤解,以及亂碼產(chǎn)生的原因和解決辦法

            3.1 容易產(chǎn)生的誤解
              對編碼的誤解
            誤解一 在將“字節(jié)串”轉(zhuǎn)化成“UNICODE 字符串”時,比如在讀取文本文件時,或者通過網(wǎng)絡(luò)傳輸文本時,容易將“字節(jié)串”簡單地作為單字節(jié)字符串,采用每“一個字節(jié)”就是“一個字符”的方法進(jìn)行轉(zhuǎn)化。

            而實際上,在非英文的環(huán)境中,應(yīng)該將“字節(jié)串”作為 ANSI 字符串,采用適當(dāng)?shù)木幋a來得到 UNICODE 字符串,有可能“多個字節(jié)”才能得到“一個字符”。

            通常,一直在英文環(huán)境下做開發(fā)的程序員們,容易有這種誤解。
            誤解二 在 DOS,Windows 98 等非 UNICODE 環(huán)境下,字符串都是以 ANSI 編碼的字節(jié)形式存在的。這種以字節(jié)形式存在的字符串,必須知道是哪種編碼才能被正確地使用。這使我們形成了一個慣性思維:“字符串的編碼”。

            當(dāng) UNICODE 被支持后,Java 中的 String 是以字符的“序號”來存儲的,不是以“某種編碼的字節(jié)”來存儲的,因此已經(jīng)不存在“字符串的編碼”這個概念了。只有在“字符串”與“字節(jié)串”轉(zhuǎn)化時,或 者,將一個“字節(jié)串”當(dāng)成一個 ANSI 字符串時,才有編碼的概念。

            不少的人都有這個誤解。

            第一種誤解,往往是導(dǎo)致亂碼產(chǎn)生的原因。第二種誤解,往往導(dǎo)致本來容易糾正的亂碼問題變得更復(fù)雜。

            在這里,我們可以看到,其中所講的“誤解一”,即采用每“一個字節(jié)”就是“一個字符”的轉(zhuǎn)化方法,實際上也就等同于采用 iso-8859-1 進(jìn)行轉(zhuǎn)化。因此,我們常常使用 bytes = string.getBytes("iso-8859-1") 來進(jìn)行逆向操作,得到原始的“字節(jié)串”。然后再使用正確的 ANSI 編碼,比如 string = new String(bytes, "GB2312"),來得到正確的“UNICODE 字符串”。

            回頁首

            3.2 非 UNICODE 程序在不同語言環(huán)境間移植時的亂碼

            非 UNICODE 程序中的字符串,都是以某種 ANSI 編碼形式存在的。如果程序運(yùn)行時的語言環(huán)境與開發(fā)時的語言環(huán)境不同,將會導(dǎo)致 ANSI 字符串的顯示失敗。

            比如,在日文環(huán)境下開發(fā)的非 UNICODE 的日文程序界面,拿到中文環(huán)境下運(yùn)行時,界面上將顯示亂碼。如果這個日文程序界面改為采用 UNICODE 來記錄字符串,那么當(dāng)在中文環(huán)境下運(yùn)行時,界面上將可以顯示正常的日文。

            由于客觀原因,有時候我們必須在中文操作系統(tǒng)下運(yùn)行非 UNICODE 的日文軟件,這時我們可以采用一些工具,比如,南極星,AppLocale 等,暫時的模擬不同的語言環(huán)境。

            回頁首

            3.3 網(wǎng)頁提交字符串

            當(dāng)頁面中的表單提交字符串時,首先把字符串按照當(dāng)前頁面的編碼,轉(zhuǎn)化成字節(jié)串。然后再將每個字節(jié)轉(zhuǎn)化成 "%XX" 的格式提交到 Web 服務(wù)器。比如,一個編碼為 GB2312 的頁面,提交 "中" 這個字符串時,提交給服務(wù)器的內(nèi)容為 "%D6%D0"。

            在服務(wù)器端,Web 服務(wù)器把收到的 "%D6%D0" 轉(zhuǎn)化成 [0xD6, 0xD0] 兩個字節(jié),然后再根據(jù) GB2312 編碼規(guī)則得到 "中" 字。

            在 Tomcat 服務(wù)器中,request.getParameter() 得到亂碼時,常常是因為前面提到的“誤解一”造成的。默認(rèn)情況下,當(dāng)提交 "%D6%D0" 給 Tomcat 服務(wù)器時,request.getParameter() 將返回 [0x00D6, 0x00D0] 兩個 UNICODE 字符,而不是返回一個 "中" 字符。因此,我們需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字節(jié)串,再用 string = new String(bytes, "GB2312") 重新得到正確的字符串 "中"。

            回頁首

            3.4 從數(shù)據(jù)庫讀取字符串

            通過數(shù)據(jù)庫客戶端(比如 ODBC 或 JDBC)從數(shù)據(jù)庫服務(wù)器中讀取字符串時,客戶端需要從服務(wù)器獲知所使用的 ANSI 編碼。當(dāng)數(shù)據(jù)庫服務(wù)器發(fā)送字節(jié)流給客戶端時,客戶端負(fù)責(zé)將字節(jié)流按照正確的編碼轉(zhuǎn)化成 UNICODE 字符串。

            如果從數(shù)據(jù)庫讀取字符串時得到亂碼,而數(shù)據(jù)庫中存放的數(shù)據(jù)又是正確的,那么往往還是因為前面提到的“誤解一”造成的。解決的辦法還是通過 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法,重新得到原始的字節(jié)串,再重新使用正確的編碼轉(zhuǎn)化成字符串。

            回頁首

            3.5 電子郵件中的字符串

            當(dāng)一段 Text 或者 HTML 通過電子郵件傳送時,發(fā)送的內(nèi)容首先通過一種指定的字符編碼轉(zhuǎn)化成“字節(jié)串”,然后再把“字節(jié)串”通過一種指定的傳輸編碼(Content-Transfer-Encoding)進(jìn)行轉(zhuǎn)化得到另一串“字節(jié)串”。比如,打開一封電子郵件源代碼,可以看到類似的內(nèi)容:

            Content-Type: text/plain;
                    charset="gb2312"
            Content-Transfer-Encoding: base64

            sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==

            最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 兩種。在對二進(jìn)制文件或者中文文本進(jìn)行轉(zhuǎn)化時,Base64 得到的“字節(jié)串”比 Quoted-Printable 更短。在對英文文本進(jìn)行轉(zhuǎn)化時,Quoted-Printable 得到的“字節(jié)串”比 Base64 更短。

            郵件的標(biāo)題,用了一種更簡短的格式來標(biāo)注“字符編碼”和“傳輸編碼”。比如,標(biāo)題內(nèi)容為 "中",則在郵件源代碼中表示為:

            // 正確的標(biāo)題格式
            Subject: =?GB2312?B?1tA=?=

            其中,

            • 第一個“=?”與“?”中間的部分指定了字符編碼,在這個例子中指定的是 GB2312。
            • “?”與“?”中間的“B”代表 Base64。如果是“Q”則代表 Quoted-Printable。
            • 最后“?”與“?=”之間的部分,就是經(jīng)過 GB2312 轉(zhuǎn)化成字節(jié)串,再經(jīng)過 Base64 轉(zhuǎn)化后的標(biāo)題內(nèi)容。

            如果“傳輸編碼”改為 Quoted-Printable,同樣,如果標(biāo)題內(nèi)容為 "中":

            // 正確的標(biāo)題格式
            Subject: =?GB2312?Q?=D6=D0?=

            如果閱讀郵件時出現(xiàn)亂碼,一般是因為“字符編碼”或“傳輸編碼”指定有誤,或者是沒有指定。比如,有的發(fā)郵件組件在發(fā)送郵件時,標(biāo)題 "中":

            // 錯誤的標(biāo)題格式
            Subject: =?ISO-8859-1?Q?=D6=D0?=

            這樣的表示,實際上是明確指明了標(biāo)題為 [0x00D6, 0x00D0],即 "ÖÐ",而不是 "中"。

            回頁首

            4. 幾種錯誤理解的糾正

            誤解:“ISO-8859-1 是國際編碼?”

            非也。iso-8859-1 只是單字節(jié)字符集中最簡單的一種,也就是“字節(jié)編號”與“UNICODE 字符編號”一致的那種編碼規(guī)則。當(dāng)我們要把一個“字節(jié)串”轉(zhuǎn)化成“字符串”,而又不知道它是哪一種 ANSI 編碼時,先暫時地把“每一個字節(jié)”作為“一個字符”進(jìn)行轉(zhuǎn)化,不會造成信息丟失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢復(fù)到原始的字節(jié)串。

            誤解:“Java 中,怎樣知道某個字符串的內(nèi)碼?”

            Java 中,字符串類 java.lang.String 處理的是 UNICODE 字符串,不是 ANSI 字符串。我們只需要把字符串作為“抽象的符號的串”來看待。因此不存在字符串的內(nèi)碼的問題。

          posted on 2008-01-17 14:29 backup2007 閱讀(551) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 镇远县| 修文县| 庆阳市| 和田县| 汶川县| 祁门县| 新干县| 贵港市| 民丰县| 普洱| 宁化县| 华宁县| 苍溪县| 贵港市| 偃师市| 烟台市| 桐城市| 泽州县| 阜新市| 赞皇县| 廊坊市| 广西| 青海省| 阜康市| 古丈县| 柳江县| 平湖市| 怀柔区| 博罗县| 北票市| 新兴县| 满城县| 安仁县| 定襄县| 宝丰县| 宁乡县| 永善县| 宝鸡市| 秦安县| 岢岚县| 出国|