qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          Java編碼及網絡傳輸中的編碼問題

          Java編碼及網絡傳輸中的編碼問題

           近來試著FTP搜索,遇到編碼問題,研究了下。

            Java內部的String為Unicode編碼,每個字符占兩個字節。

            Java編解碼方法如下:

          1. String str = "hi好啊me";  
          2. byte[] gbkBytes=str.getBytes("GBK");//將String的Unicode編碼轉為GBK編碼,輸出到字節中 
          3. String string=new String(gbkBytes,"GBK");//gbkBytes中的字節流以GBK方案解碼成Unicode形式的Java字符串

            1、表單數據的編碼

            現在的問題是,在網絡中,不知道客戶端發過來的字節流的編碼方案(發送前瀏覽器會對數據編碼!!!各個瀏覽器還不一樣!!!)

            解決方案如下:

            當然URLEncoder.encode(str, "utf-8")和URLDecoder.decode(strReceive,"utf-8")方法中的編碼方案要一致。

            2、網址的編碼

            但以上方法只適合表單數據的提交;對于URL則不行!!!原因是URLEncoder把'/'也編碼了,瀏覽器發送時報錯!!!那么,只要http://IP/子目錄把http://IP/這部分原封不動(當然這部分不要有中文),之后的數據以'/'分割后分段編碼即可。

            代碼如下:

          1. /** 
          2.  * 對{@link URLEncoder#encode(String, String)}的封裝,但不編碼'/'字符,對其他字符分段編碼 
          3.  *  
          4.  * @param str 
          5.  *            要編碼的URL 
          6.  * @param encoding 
          7.  *            編碼格式 
          8.  * @return 字符串以字符'/'隔開,對每一段單獨編碼以encoding編碼格式編碼 
          9.  * @version: 2012_01_10 
          10.  *           <p> 
          11.  *           注意:未考慮':',如直接對http://編解碼,會產生錯誤!!!請在使用前將其分離出來,可以使用 
          12.  *           {@link #encodeURLAfterHost(String, String)}方法解決此問題 
          13.  *           <p> 
          14.  *           注意:對字符/一起編碼,導致URL請求異常!! 
          15.  */ 
          16. public static String encodeURL(String str, String encoding) {  
          17.     final char splitter = '/';  
          18.     try {  
          19.         StringBuilder sb = new StringBuilder(2 * str.length());  
          20.         int start = 0;  
          21.         for (int i = 0; i < str.length(); i++) {  
          22.             if (str.charAt(i) == splitter) {  
          23.                 sb.append(URLEncoder.encode(str.substring(start, i),  
          24.                         encoding));  
          25.                 sb.append(splitter);  
          26.                 start = i + 1;  
          27.             }  
          28.         }  
          29.         if (start < str.length())  
          30.             sb.append(URLEncoder.encode(str.substring(start), encoding));  
          31.         return sb.toString();  
          32.     } catch (UnsupportedEncodingException e) {  
          33.         e.printStackTrace();  
          34.     }  
          35.     return null;  
          36. }  
          37. /** 
          38.  * 對IP地址后的URL通過'/'分割后進行分段編碼. 
          39.  * <p> 
          40.  * 對{@link URLEncoder#encode(String, String)} 
          41.  * 的封裝,但不編碼'/'字符,也不編碼網站部分(如ftp://a.b.c.d/部分,檢測方法為對三個'/'字符的檢測,且要求前兩個連續), 
          42.  * 對其他字符分段編碼 
          43.  *  
          44.  * @param str 
          45.  *            要編碼的URL 
          46.  * @param encoding 
          47.  *            編碼格式 
          48.  * @return IP地址后字符串以字符'/'隔開,對每一段單獨編碼以encoding編碼格式編碼,其他部分不變 
          49.  * @version: 2012_01_10 
          50.  *           <p> 
          51.  *           注意:對字符/一起編碼,導致URL請求異常!! 
          52.  */ 
          53. public static String encodeURLAfterHost(String str, String encoding) {  
          54.     final char splitter = '/';  
          55.     int index = str.indexOf(splitter);//第一個'/'的位置 
          56.     index++;//移到下一位置!! 
          57.     if (index < str.length() && str.charAt(index) == splitter) {//檢測第一個'/'之后是否還是'/',如ftp:// 
          58.         index++;//從下一個開始 
          59.         index = str.indexOf(splitter, index);//第三個'/';如ftp://anonymous:tmp@g.cn:219.223.168.20/中的最后一個'/' 
          60.         if (index > 0) {  
          61.             return str.substring(0, index + 1)  
          62.                     + encodeURL(str.substring(index + 1), encoding);//如ftp://anonymous:tmp@g.cn:219.223.168.20/天空 
          63.         } else 
          64.             return str;//如ftp://anonymous:tmp@g.cn:219.223.168.20 
          65.     }  
          66.     return encodeURL(str, encoding);  
          67. }  
          68. /** 
          69.  * 對IP地址后的URL通過'/'分割后進行分段編碼. 
          70.  * 此方法與{@link #decodeURLAfterHost(String, String)}配對使用 
          71.  * @param str 
          72.  *            要解碼的URL 
          73.  * @param encoding 
          74.  *            str的編碼格式 
          75.  * @return IP地址后字符串以字符'/'隔開,對每一段單獨解碼以encoding編碼格式解碼,其他部分不變 
          76.  * @version: 2012_01_10 
          77.  *  
          78.  *           <p> 
          79.  *           注意:對字符/一起解碼,將導致URL請求異常!! 
          80.  */ 
          81. public static String decodeURLAfterHost(String str, String encoding) {  
          82.     final char splitter = '/';  
          83.     int index = str.indexOf(splitter);//第一個'/'的位置 
          84.     index++;//移到下一位置!! 
          85.     if (index < str.length() && str.charAt(index) == splitter) {//檢測第一個'/'之后是否還是'/',如ftp:// 
          86.         index++;//從下一個開始 
          87.         index = str.indexOf(splitter, index);//第三個'/';如ftp://anonymous:tmp@g.cn:219.223.168.20/中的最后一個'/' 
          88.         if (index > 0) {  
          89.             return str.substring(0, index + 1)  
          90.                     + decodeURL(str.substring(index + 1), encoding);//如ftp://anonymous:tmp@g.cn:219.223.168.20/天空 
          91.         } else 
          92.             return str;//如ftp://anonymous:tmp@g.cn:219.223.168.20 
          93.     }  
          94.     return decodeURL(str, encoding);  
          95. }  
          96. /** 
          97.  * 此方法與{@link #encodeURL(String, String)}配對使用 
          98.  * <p> 
          99.  * 對{@link URLDecoder#decode(String, String)}的封裝,但不解碼'/'字符,對其他字符分段解碼 
          100.  *  
          101.  * @param str 
          102.  *            要解碼的URL 
          103.  * @param encoding 
          104.  *            str的編碼格式 
          105.  * @return 字符串以字符'/'隔開,對每一段單獨編碼以encoding編碼格式解碼 
          106.  * @version: 2012_01_10 
          107.  *  
          108.  *           <p> 
          109.  *           注意:對字符/一起編碼,導致URL請求異常!! 
          110.  */ 
          111. public static String decodeURL(String str, String encoding) {  
          112.     final char splitter = '/';  
          113.     try {  
          114.         StringBuilder sb = new StringBuilder(str.length());  
          115.         int start = 0;  
          116.         for (int i = 0; i < str.length(); i++) {  
          117.             if (str.charAt(i) == splitter) {  
          118.                 sb.append(URLDecoder.decode(str.substring(start, i),  
          119.                         encoding));  
          120.                 sb.append(splitter);  
          121.                 start = i + 1;  
          122.             }  
          123.         }  
          124.         if (start < str.length())  
          125.             sb.append(URLDecoder.decode(str.substring(start), encoding));  
          126.         return sb.toString();  
          127.     } catch (UnsupportedEncodingException e) {  
          128.         e.printStackTrace();  
          129.     }  
          130.     return null;  
          131. }

           3、亂碼了還能恢復?

            問題如下:

            貌似圖中的utf-8改成iso8859-1是可以的,utf-8在字符串中有中文時不行(但英文部分仍可正確解析)!!!畢竟GBK的字節流對于utf-8可能是無效的,碰到無效的字符怎么解析,是否可逆那可不好說啊。

            測試代碼如下:

          1. package tests;  
          2.  
          3. import java.io.UnsupportedEncodingException;  
          4. import java.net.URLEncoder;  
          5.  
          6. /**  
          7.  * @author LC  
          8.  * @version: 2012_01_12  
          9.  */ 
          10. public class TestEncoding {  
          11.     static String utf8 = "utf-8";  
          12.     static String iso = "iso-8859-1";  
          13.     static String gbk = "GBK";  
          14.  
          15.     public static void main(String[] args) throws UnsupportedEncodingException {  
          16.         String str = "hi好啊me";  
          17.         //      System.out.println("?的十六進制為:3F");  
          18.         //      System.err  
          19.         //              .println("出現中文時,如果編碼方案不支持中文,每個字符都會被替換為?的對應編碼!(如在iso-8859-1中)");  
          20.         System.out.println("原始字符串:\t\t\t\t\t\t" + str);  
          21.         String utf8_encoded = URLEncoder.encode(str, "utf-8");  
          22.         System.out.println("用URLEncoder.encode()方法,并用UTF-8編碼后:\t\t" + utf8_encoded);  
          23.         String gbk_encoded = URLEncoder.encode(str, "GBK");  
          24.         System.out.println("用URLEncoder.encode()方法,并用GBK編碼后:\t\t" + gbk_encoded);  
          25.         testEncoding(str, utf8, gbk);  
          26.         testEncoding(str, gbk, utf8);  
          27.         testEncoding(str, gbk, iso);  
          28.         printBytesInDifferentEncoding(str);  
          29.         printBytesInDifferentEncoding(utf8_encoded);  
          30.         printBytesInDifferentEncoding(gbk_encoded);  
          31.     }  
          32.  
          33.     /**  
          34.      * 測試用錯誤的編碼方案解碼后再編碼,是否對原始數據有影響  
          35.      *   
          36.      * @param str  
          37.      *            輸入字符串,Java的String類型即可  
          38.      * @param encodingTrue  
          39.      *            編碼方案1,用于模擬原始數據的編碼  
          40.      * @param encondingMidian  
          41.      *            編碼方案2,用于模擬中間的編碼方案  
          42.      * @throws UnsupportedEncodingException  
          43.      */ 
          44.     public static void testEncoding(String str, String encodingTrue,  
          45.             String encondingMidian) throws UnsupportedEncodingException {  
          46.         System.out.println();  
          47.         System.out  
          48.                 .printf("%s編碼的字節數據->用%s解碼并轉為Unicode編碼的JavaString->用%s解碼變為字節流->讀入Java(用%s解碼)后變為Java的String\n",  
          49.                         encodingTrue, encondingMidian, encondingMidian,  
          50.                         encodingTrue);  
          51.         System.out.println("原始字符串:\t\t" + str);  
          52.         byte[] trueEncodingBytes = str.getBytes(encodingTrue);  
          53.         System.out.println("原始字節流:\t\t" + bytesToHexString(trueEncodingBytes)  
          54.                 + "\t\t//即用" + encodingTrue + "編碼后的字節流");  
          55.         String encodeUseMedianEncoding = new String(trueEncodingBytes,  
          56.                 encondingMidian);  
          57.         System.out.println("中間字符串:\t\t" + encodeUseMedianEncoding + "\t\t//即用" 
          58.                 + encondingMidian + "解碼原始字節流后的字符串");  
          59.         byte[] midianBytes = encodeUseMedianEncoding.getBytes("Unicode");  
          60.         System.out.println("中間字節流:\t\t" + bytesToHexString(midianBytes)  
          61.                 + "\t\t//即中間字符串對應的Unicode字節流(和Java內存數據一致)");  
          62.         byte[] redecodedBytes = encodeUseMedianEncoding  
          63.                 .getBytes(encondingMidian);  
          64.         System.out.println("解碼字節流:\t\t" + bytesToHexString(redecodedBytes)  
          65.                 + "\t\t//即用" + encodingTrue + "解碼中間字符串(流)后的字符串");  
          66.         String restored = new String(redecodedBytes, encodingTrue);  
          67.         System.out.println("解碼字符串:\t\t" + restored + "\t\t和原始數據相同?  " 
          68.                 + restored.endsWith(str));  
          69.     }  
          70.  
          71.     /**  
          72.      * 將字符串分別編碼為GBK、UTF-8、iso-8859-1的字節流并輸出  
          73.      *   
          74.      * @param str  
          75.      * @throws UnsupportedEncodingException  
          76.      */ 
          77.     public static void printBytesInDifferentEncoding(String str)  
          78.             throws UnsupportedEncodingException {  
          79.         System.out.println("");  
          80.         System.out.println("原始String:\t\t" + str + "\t\t長度為:" + str.length());  
          81.         String unicodeBytes = bytesToHexString(str.getBytes("unicode"));  
          82.         System.out.println("Unicode bytes:\t\t" + unicodeBytes);  
          83.         String gbkBytes = bytesToHexString(str.getBytes("GBK"));  
          84.         System.out.println("GBK bytes:\t\t" + gbkBytes);  
          85.         String utf8Bytes = bytesToHexString(str.getBytes("utf-8"));  
          86.         System.out.println("UTF-8 bytes:\t\t" + utf8Bytes);  
          87.         String iso8859Bytes = bytesToHexString(str.getBytes("iso-8859-1"));  
          88.         System.out.println("iso8859-1 bytes:\t" + iso8859Bytes + "\t\t長度為:" 
          89.                 + iso8859Bytes.length() / 3);  
          90.         System.out.println("可見Unicode在之前加了兩個字節FE FF,之后則每個字符兩字節");  
          91.     }  
          92.  
          93.     /**  
          94.      * 將該數組轉的每個byte轉為兩位的16進制字符,中間用空格隔開  
          95.      *   
          96.      * @param bytes  
          97.      *            要轉換的byte序列  
          98.      * @return 轉換后的字符串  
          99.      */ 
          100.     public static final String bytesToHexString(byte[] bytes) {  
          101.         StringBuilder sb = new StringBuilder(bytes.length * 2);  
          102.         for (int i = 0; i < bytes.length; i++) {  
          103.             String hex = Integer.toHexString(bytes[i] & 0xff);// &0xff是byte小于0時會高位補1,要改回0  
          104.             if (hex.length() == 1)  
          105.                 sb.append('0');  
          106.             sb.append(hex);  
          107.             sb.append(" ");  
          108.         }  
          109.         return sb.toString().toUpperCase();  
          110.     }  
          111. }



          posted on 2012-01-17 11:30 順其自然EVO 閱讀(2582) 評論(0)  編輯  收藏


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


          網站導航:
           
          <2012年1月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 抚宁县| 鄱阳县| 南投市| 稷山县| 宣城市| 海盐县| 大姚县| 芦溪县| 普宁市| 盐亭县| 阿拉尔市| 青冈县| 林州市| 株洲县| 兴业县| 武鸣县| 霞浦县| 沈阳市| 斗六市| 年辖:市辖区| 安陆市| 罗田县| 平陆县| 施秉县| 伊川县| 唐海县| 黄石市| 霍林郭勒市| 永靖县| 古丈县| 任丘市| 南昌市| 巴南区| 合作市| 河东区| 航空| 五寨县| 安国市| 琼中| 察哈| 江口县|