程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java編碼及網絡傳輸中的編碼問題

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