posts - 40, comments - 58, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Java 中 byte與char、String互轉原理

          Posted on 2010-11-12 12:08 Astro.Qi 閱讀(974) 評論(0)  編輯  收藏 所屬分類: Java
          一、字節和unicode 

          Java內核是unicode的,就連class文件也是,但是很多媒體,包括文件/流的保存方式是使用字節流的。因此Java要對這些字節流經行轉化。 char是unicode的,而byte是字節。Java中 byte/char互轉的函數在sun.io的包中間有。其中ByteToCharConverter類是中調度,可以用來告訴你,你用的 convertor。其中兩個很常用的靜態函數是: 

          public static ByteToCharConverter getDefault(); 
          public static ByteToCharConverter getConverter(String encoding); 



          如果你不指定converter,則系統會自動使用當前的encoding,gb平臺上用gbk,en平臺上用8859_1。 

          byte ——〉char: 
          ""的gb碼是:0xc4e3 ,unicode是0x4f60 
          String encoding 
          = "gb2312"
          byte b[] = {(byte)'\u00c4',(byte)'\u00e3'}; 
          ByteToCharConverter converter 
          = ByteToCharConverter.getConverter(encoding); 
          char c[] = converter.convertAll(b); 
          for (int i = 0; i < c.length; i++) { 
          System.out.println(Integer.toHexString(c[i])); 

          結果是什么?
          0x4f60 
          如果encoding 
          ="8859_1",結果又是什么?0x00c4,0x00e3 



          如果代碼改為: 

          byte b[] = {(byte)'\u00c4',(byte)'\u00e3'}; 
          ByteToCharConverter converter 
          = ByteToCharConverter. getDefault(); 
          char c[] = converter.convertAll(b); 
          for (int i = 0; i < c.length; i++) { 
          System.out.println(Integer.toHexString(c[i])); 




          結果將又是什么? 

          這就要根據平臺的編碼而定。 

          char ——〉byte: 
          String encoding 
          = "gb2312"
          char c[] = {'\u4f60'}; 
          CharToByteConverter converter 
          = CharToByteConverter.getConverter(encoding); 
          byte b[] = converter.convertAll(c); 
          for (int i = 0; i < b.length; i++) { 
          System.out.println(Integer.toHexString(b[i])); 

          結果是什么?
          0x00c4,0x00e3 
          如果encoding 
          ="8859_1",結果又是什么?0x3f 
          如果代碼改為 
          String encoding 
          = "gb2312"
          char c[] = {'\u4f60'}; 
          CharToByteConverter converter 
          = CharToByteConverter.getDefault(); 
          byte b[] = converter.convertAll(c); 
          for (int i = 0; i < b.length; i++) { 
          System.out.println(Integer.toHexString(b[i])); 




          結果將又是什么?還是根據平臺的編碼而定。 

          很多中文問題就是從這兩個最簡單的類派生出來的。而卻有很多類不直接支持把encoding輸入,這給我們帶來諸多不便。很多程序難得用encoding了,直接用default的encoding,這就給我們移植帶來了很多困難。 

          二、utf-8 

          utf
          -8是和unicode一一對應的,其實現很簡單: 

          7位的unicode: 
          0 _ _ _ _ _ _ _ 
          11位的unicode: 
          1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _ 
          16位的unicode: 
          1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 
          21位的unicode: 
          1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 



          大多數情況是只使用到16位以下的unicode: 

          ""的gb碼是:0xc4e3 ,unicode是0x4f60 
          0xc4e3的二進制: 
          1100 ,0100 ,1110 ,0011 



          由于只有兩位我們按照兩位的編碼來排,但是我們發現這行不通,因為第7位不是0因此,返回
          "?" 

          0x4f60的二進制: 
          0100 ,1111 ,0110 ,0000 
          我們用utf
          -8補齊,變成: 
          1110 ,0100 ,1011 ,1101 ,1010 ,0000 
          e4
          --bd-- a0 
          于是返回:
          0xe4,0xbd,0xa0。 



          三、string和byte[] 

          string其實核心是char[],然而要把byte轉化成string,必須經過編碼。string.length()其實就是char數組的長度,如果使用不同的編碼,很可能會錯分,造成散字和亂碼。例如: 

          String encoding 
          = “”; 
          byte [] b={(byte)'\u00c4',(byte)'\u00e3'}; 
          String str
          =new String(b,encoding); 



          如果encoding
          =8859_1,會有兩個字,但是encoding=gb2312只有一個字這個問題在處理分頁是經常發生。 

          四、Reader,Writer / InputStream,OutputStream 

          Reader和Writer核心是char,InputStream和OutputStream核心是byte。但是Reader和Writer的主要目的是要把char讀
          /寫InputStream/OutputStream。例如: 

          文件test.txt只有一個
          ""字,0xc4,0xe3 
          String encoding 
          = "gb2312"
          InputStreamReader reader 
          = new InputStreamReader(new FileInputStream( 
          "text.txt"), encoding); 
          char c[] = new char[10]; 
          int length = reader.read(c); 
          for (int i = 0; i < length; i++) { 
          System.out.println(c[i]); 




          結果是什么?是
          ""。如果encoding ="8859_1",結果是什么?"??"兩個字符,表示不認識。反過來的例子自己做。 
          五、我們要對Java的編譯器有所了解: 

          Javac 
          ?encoding 



          我們常常沒有用到encoding這個參數。其實encoding這個參數對于跨平臺的操作是很重要的。如果沒有指定encoding,則按照系統的默認 encoding,gb平臺上是gb2312,英文平臺上是iso8859_1。Java的編譯器實際上是調用sun.tools.Javac.main 的類,對文件進行編譯,這個類有compile函數中間有一個encoding的變量,
          -encoding的參數其實直接傳給encoding變量。編譯器就是根據這個變量來讀取Java文件的,然后把用utf-8形式編譯成class文件。例子代碼: 

          String str 
          = ""
          FileWriter writer 
          = new FileWriter("text.txt"); 
          write.write(str); 
          writer.close(); 

          如果用gb2312編譯,你會找到e4 bd a0的字段 ; 
          如果用8859_1編譯, 00c4 00e3的二進制: 
          00000000 ,11000100 ,00000000 ,11100011 
          因為每個字符都大于7位,因此用11位編碼: 
          11000001100001001100001110100011 
          c1
          -- 84-- c3--  a3 
          你會找到c1 
          84 c3 a3 



          但是我們往往忽略掉這個參數,因此這樣往往會有跨平臺的問題: 

          樣例代碼在中文平臺上編譯,生成zhclass 

          樣例代碼在英文平臺上編譯,輸出enclass 

          (
          1) zhclass在中文平臺上執行ok,但是在英文平臺上不行 

          (
          2) enclass在英文平臺上執行ok,但是在中文平臺上不行 

          原因是: 

          (
          1) 在中文平臺上編譯后,其實str在運行態的char[]是0x4f60, 在中文平臺上運行,filewriter的缺省編碼是gb2312,因此 chartobyteconverter會自動用調用gb2312的converter,把str轉化成byte輸入到fileoutputstream 中,于是0xc4,0xe3放進了文件。但是如果是在英文平臺下,chartobyteconverter的缺省值是8859_1, filewriter會自動調用8859_1去轉化str,但是他無法解釋,因此他會輸出"?" 

          (
          2) 在英文平臺上編譯后,其實str在運行態的char[]是0x00c4 0x00e3, 在中文平臺上運行,中文無法識別,因此會出現??;在英文平臺上,0x00c4-->0xc4,0x00e3->0xe3,因此 0xc4,0xe3被放進了文件。 

          六、其它原因: 

          <%@ page contentType="text/html; charset=GBK" %> 



          設置瀏覽器的顯示編碼,如果response的數據是utf8編碼,顯示將是亂碼,但是亂碼和上述原因還不一樣。 

          七、發生編碼的地方: 

          1. 從數據庫到Java程序 byte——〉char 

          2. 從Java程序到數據庫 char——〉byte 

          3. 從文件到Java程序 byte——〉char 

          4. 從Java程序到文件 char——〉byte 

          5. 從Java程序到頁面顯示 char——〉byte 

          6. 從頁面form提交數據到Java程序byte——〉char 

          7. 從流到Java程序byte——〉char 

          8. 從Java程序到流char——〉byte 

          可以使用配置過濾器的方法解決中文亂碼的: 

          <web-app> 
          <filter> 
          <filter-name>RequestFilter</filter-name> 
          <filter-class>net.golden.uirs.util.RequestFilter</filter-class> 
          <init-param> 
          <param-name>charset</param-name> 
          <param-value>gb2312</param-value> 
          </init-param> 
          </filter> 
          <filter-mapping> 
          <filter-name>RequestFilter</filter-name> 
          <url-pattern>*.Jsp</url-pattern> 
          </filter-mapping> 
          </web-app> 


          public void doFilter(ServletRequest req, ServletResponse res, 
          FilterChain fChain) 
          throws IOException, ServletException { 
          HttpServletRequest request 
          = (HttpServletRequest) req; 
          HttpServletResponse response 
          = (HttpServletResponse) res; 
          HttpSession session 
          = request.getSession(); 
          String userId 
          = (String) session.getAttribute("userid"); 
          req.setCharacterEncoding(
          this.filterConfig.getInitParameter("charset")); 
          // 設置字符集? 
          //實際上是設置了byte ——〉char的encoding 
          try { 
          if (userId == null || userId.equals("")) { 
          if (!request.getRequestURL().toString().matches( 
          ".*/uirs/logon/logon(Controller){0,1}\\x2EJsp$")) { 
          session.invalidate(); 
          response.sendRedirect(request.getContextPath() 
          + 
          "/uirs/logon/logon.Jsp"); 


          else { 
          // 看看是否具有信息上報系統的權限 
          if (!net.golden.uirs.util.UirsChecker.check(userId, "信息上報系統"
          net.golden.uirs.util.UirsChecker.ACTION_DO)) { 
          if (!request.getRequestURL().toString().matches( 
          ".*/uirs/logon/logon(Controller){0,1}\\x2EJsp$")) { 
          response.sendRedirect(request.getContextPath() 
          + 
          "/uirs/logon/logonController.Jsp"); 




          catch (Exception ex) { 
          response.sendRedirect(request.getContextPath() 
          +"/uirs/logon/logon.Jsp"); 

          fChain.doFilter(req, res); 
          主站蜘蛛池模板: 巩留县| 翁牛特旗| 清远市| 大宁县| 永昌县| 澎湖县| 丰城市| 桦川县| 金寨县| 荆州市| 虹口区| 饶河县| 贵德县| 宜宾县| 孙吴县| 富平县| 潜山县| 原平市| 揭东县| 类乌齐县| 镇江市| 玉溪市| 科技| 松潘县| 灵武市| 临江市| 饶平县| 玉林市| 余庆县| 桃园县| 商都县| 长春市| 永吉县| 新巴尔虎左旗| 马公市| 务川| 曲松县| 衡阳县| 潍坊市| 武强县| 中江县|