Java編碼及網(wǎng)絡(luò)傳輸中的編碼問(wèn)題
近來(lái)試著FTP搜索,遇到編碼問(wèn)題,研究了下。
Java內(nèi)部的String為Unicode編碼,每個(gè)字符占兩個(gè)字節(jié)。
Java編解碼方法如下:
- String str = "hi好啊me";
- byte[] gbkBytes=str.getBytes("GBK");//將String的Unicode編碼轉(zhuǎn)為GBK編碼,輸出到字節(jié)中
- String string=new String(gbkBytes,"GBK");//gbkBytes中的字節(jié)流以GBK方案解碼成Unicode形式的Java字符串
1、表單數(shù)據(jù)的編碼
現(xiàn)在的問(wèn)題是,在網(wǎng)絡(luò)中,不知道客戶端發(fā)過(guò)來(lái)的字節(jié)流的編碼方案(發(fā)送前瀏覽器會(huì)對(duì)數(shù)據(jù)編碼?。?!各個(gè)瀏覽器還不一樣!?。。?/p>
解決方案如下:
當(dāng)然URLEncoder.encode(str, "utf-8")和URLDecoder.decode(strReceive,"utf-8")方法中的編碼方案要一致。
2、網(wǎng)址的編碼
但以上方法只適合表單數(shù)據(jù)的提交;對(duì)于URL則不行!??!原因是URLEncoder把'/'也編碼了,瀏覽器發(fā)送時(shí)報(bào)錯(cuò)?。?!那么,只要http://IP/子目錄把http://IP/這部分原封不動(dòng)(當(dāng)然這部分不要有中文),之后的數(shù)據(jù)以'/'分割后分段編碼即可。
代碼如下:
- /**
- * 對(duì){@link URLEncoder#encode(String, String)}的封裝,但不編碼'/'字符,對(duì)其他字符分段編碼
- *
- * @param str
- * 要編碼的URL
- * @param encoding
- * 編碼格式
- * @return 字符串以字符'/'隔開(kāi),對(duì)每一段單獨(dú)編碼以encoding編碼格式編碼
- * @version: 2012_01_10
- * <p>
- * 注意:未考慮':',如直接對(duì)http://編解碼,會(huì)產(chǎn)生錯(cuò)誤?。?!請(qǐng)?jiān)谑褂们皩⑵浞蛛x出來(lái),可以使用
- * {@link #encodeURLAfterHost(String, String)}方法解決此問(wèn)題
- * <p>
- * 注意:對(duì)字符/一起編碼,導(dǎo)致URL請(qǐng)求異常??!
- */
- public static String encodeURL(String str, String encoding) {
- final char splitter = '/';
- try {
- StringBuilder sb = new StringBuilder(2 * str.length());
- int start = 0;
- for (int i = 0; i < str.length(); i++) {
- if (str.charAt(i) == splitter) {
- sb.append(URLEncoder.encode(str.substring(start, i),
- encoding));
- sb.append(splitter);
- start = i + 1;
- }
- }
- if (start < str.length())
- sb.append(URLEncoder.encode(str.substring(start), encoding));
- return sb.toString();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return null;
- }
- /**
- * 對(duì)IP地址后的URL通過(guò)'/'分割后進(jìn)行分段編碼.
- * <p>
- * 對(duì){@link URLEncoder#encode(String, String)}
- * 的封裝,但不編碼'/'字符,也不編碼網(wǎng)站部分(如ftp://a.b.c.d/部分,檢測(cè)方法為對(duì)三個(gè)'/'字符的檢測(cè),且要求前兩個(gè)連續(xù)),
- * 對(duì)其他字符分段編碼
- *
- * @param str
- * 要編碼的URL
- * @param encoding
- * 編碼格式
- * @return IP地址后字符串以字符'/'隔開(kāi),對(duì)每一段單獨(dú)編碼以encoding編碼格式編碼,其他部分不變
- * @version: 2012_01_10
- * <p>
- * 注意:對(duì)字符/一起編碼,導(dǎo)致URL請(qǐng)求異常!!
- */
- public static String encodeURLAfterHost(String str, String encoding) {
- final char splitter = '/';
- int index = str.indexOf(splitter);//***個(gè)'/'的位置
- index++;//移到下一位置!!
- if (index < str.length() && str.charAt(index) == splitter) {//檢測(cè)***個(gè)'/'之后是否還是'/',如ftp://
- index++;//從下一個(gè)開(kāi)始
- index = str.indexOf(splitter, index);//第三個(gè)'/';如ftp://anonymous:tmp@g.cn:219.223.168.20/中的***一個(gè)'/'
- if (index > 0) {
- return str.substring(0, index + 1)
- + encodeURL(str.substring(index + 1), encoding);//如ftp://anonymous:tmp@g.cn:219.223.168.20/天空
- } else
- return str;//如ftp://anonymous:tmp@g.cn:219.223.168.20
- }
- return encodeURL(str, encoding);
- }
- /**
- * 對(duì)IP地址后的URL通過(guò)'/'分割后進(jìn)行分段編碼.
- * 此方法與{@link #decodeURLAfterHost(String, String)}配對(duì)使用
- * @param str
- * 要解碼的URL
- * @param encoding
- * str的編碼格式
- * @return IP地址后字符串以字符'/'隔開(kāi),對(duì)每一段單獨(dú)解碼以encoding編碼格式解碼,其他部分不變
- * @version: 2012_01_10
- *
- * <p>
- * 注意:對(duì)字符/一起解碼,將導(dǎo)致URL請(qǐng)求異常??!
- */
- public static String decodeURLAfterHost(String str, String encoding) {
- final char splitter = '/';
- int index = str.indexOf(splitter);//***個(gè)'/'的位置
- index++;//移到下一位置??!
- if (index < str.length() && str.charAt(index) == splitter) {//檢測(cè)***個(gè)'/'之后是否還是'/',如ftp://
- index++;//從下一個(gè)開(kāi)始
- index = str.indexOf(splitter, index);//第三個(gè)'/';如ftp://anonymous:tmp@g.cn:219.223.168.20/中的***一個(gè)'/'
- if (index > 0) {
- return str.substring(0, index + 1)
- + decodeURL(str.substring(index + 1), encoding);//如ftp://anonymous:tmp@g.cn:219.223.168.20/天空
- } else
- return str;//如ftp://anonymous:tmp@g.cn:219.223.168.20
- }
- return decodeURL(str, encoding);
- }
- /**
- * 此方法與{@link #encodeURL(String, String)}配對(duì)使用
- * <p>
- * 對(duì){@link URLDecoder#decode(String, String)}的封裝,但不解碼'/'字符,對(duì)其他字符分段解碼
- *
- * @param str
- * 要解碼的URL
- * @param encoding
- * str的編碼格式
- * @return 字符串以字符'/'隔開(kāi),對(duì)每一段單獨(dú)編碼以encoding編碼格式解碼
- * @version: 2012_01_10
- *
- * <p>
- * 注意:對(duì)字符/一起編碼,導(dǎo)致URL請(qǐng)求異常??!
- */
- public static String decodeURL(String str, String encoding) {
- final char splitter = '/';
- try {
- StringBuilder sb = new StringBuilder(str.length());
- int start = 0;
- for (int i = 0; i < str.length(); i++) {
- if (str.charAt(i) == splitter) {
- sb.append(URLDecoder.decode(str.substring(start, i),
- encoding));
- sb.append(splitter);
- start = i + 1;
- }
- }
- if (start < str.length())
- sb.append(URLDecoder.decode(str.substring(start), encoding));
- return sb.toString();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return null;
- }
3、亂碼了還能恢復(fù)?
問(wèn)題如下:
貌似圖中的utf-8改成iso8859-1是可以的,utf-8在字符串中有中文時(shí)不行(但英文部分仍可正確解析)!??!畢竟GBK的字節(jié)流對(duì)于utf-8可能是無(wú)效的,碰到無(wú)效的字符怎么解析,是否可逆那可不好說(shuō)啊。
測(cè)試代碼如下:
- package tests;
- import java.io.UnsupportedEncodingException;
- import java.net.URLEncoder;
- /**
- * @author LC
- * @version: 2012_01_12
- */
- public class TestEncoding {
- static String utf8 = "utf-8";
- static String iso = "iso-8859-1";
- static String gbk = "GBK";
- public static void main(String[] args) throws UnsupportedEncodingException {
- String str = "hi好啊me";
- // System.out.println("?的十六進(jìn)制為:3F");
- // System.err
- // .println("出現(xiàn)中文時(shí),如果編碼方案不支持中文,每個(gè)字符都會(huì)被替換為?的對(duì)應(yīng)編碼!(如在iso-8859-1中)");
- System.out.println("原始字符串:\t\t\t\t\t\t" + str);
- String utf8_encoded = URLEncoder.encode(str, "utf-8");
- System.out.println("用URLEncoder.encode()方法,并用UTF-8編碼后:\t\t" + utf8_encoded);
- String gbk_encoded = URLEncoder.encode(str, "GBK");
- System.out.println("用URLEncoder.encode()方法,并用GBK編碼后:\t\t" + gbk_encoded);
- testEncoding(str, utf8, gbk);
- testEncoding(str, gbk, utf8);
- testEncoding(str, gbk, iso);
- printBytesInDifferentEncoding(str);
- printBytesInDifferentEncoding(utf8_encoded);
- printBytesInDifferentEncoding(gbk_encoded);
- }
- /**
- * 測(cè)試用錯(cuò)誤的編碼方案解碼后再編碼,是否對(duì)原始數(shù)據(jù)有影響
- *
- * @param str
- * 輸入字符串,Java的String類型即可
- * @param encodingTrue
- * 編碼方案1,用于模擬原始數(shù)據(jù)的編碼
- * @param encondingMidian
- * 編碼方案2,用于模擬中間的編碼方案
- * @throws UnsupportedEncodingException
- */
- public static void testEncoding(String str, String encodingTrue,
- String encondingMidian) throws UnsupportedEncodingException {
- System.out.println();
- System.out
- .printf("%s編碼的字節(jié)數(shù)據(jù)->用%s解碼并轉(zhuǎn)為Unicode編碼的JavaString->用%s解碼變?yōu)樽止?jié)流->讀入Java(用%s解碼)后變?yōu)镴ava的String\n",
- encodingTrue, encondingMidian, encondingMidian,
- encodingTrue);
- System.out.println("原始字符串:\t\t" + str);
- byte[] trueEncodingBytes = str.getBytes(encodingTrue);
- System.out.println("原始字節(jié)流:\t\t" + bytesToHexString(trueEncodingBytes)
- + "\t\t//即用" + encodingTrue + "編碼后的字節(jié)流");
- String encodeUseMedianEncoding = new String(trueEncodingBytes,
- encondingMidian);
- System.out.println("中間字符串:\t\t" + encodeUseMedianEncoding + "\t\t//即用"
- + encondingMidian + "解碼原始字節(jié)流后的字符串");
- byte[] midianBytes = encodeUseMedianEncoding.getBytes("Unicode");
- System.out.println("中間字節(jié)流:\t\t" + bytesToHexString(midianBytes)
- + "\t\t//即中間字符串對(duì)應(yīng)的Unicode字節(jié)流(和Java內(nèi)存數(shù)據(jù)一致)");
- byte[] redecodedBytes = encodeUseMedianEncoding
- .getBytes(encondingMidian);
- System.out.println("解碼字節(jié)流:\t\t" + bytesToHexString(redecodedBytes)
- + "\t\t//即用" + encodingTrue + "解碼中間字符串(流)后的字符串");
- String restored = new String(redecodedBytes, encodingTrue);
- System.out.println("解碼字符串:\t\t" + restored + "\t\t和原始數(shù)據(jù)相同? "
- + restored.endsWith(str));
- }
- /**
- * 將字符串分別編碼為GBK、UTF-8、iso-8859-1的字節(jié)流并輸出
- *
- * @param str
- * @throws UnsupportedEncodingException
- */
- public static void printBytesInDifferentEncoding(String str)
- throws UnsupportedEncodingException {
- System.out.println("");
- System.out.println("原始String:\t\t" + str + "\t\t長(zhǎng)度為:" + str.length());
- String unicodeBytes = bytesToHexString(str.getBytes("unicode"));
- System.out.println("Unicode bytes:\t\t" + unicodeBytes);
- String gbkBytes = bytesToHexString(str.getBytes("GBK"));
- System.out.println("GBK bytes:\t\t" + gbkBytes);
- String utf8Bytes = bytesToHexString(str.getBytes("utf-8"));
- System.out.println("UTF-8 bytes:\t\t" + utf8Bytes);
- String iso8859Bytes = bytesToHexString(str.getBytes("iso-8859-1"));
- System.out.println("iso8859-1 bytes:\t" + iso8859Bytes + "\t\t長(zhǎng)度為:"
- + iso8859Bytes.length() / 3);
- System.out.println("可見(jiàn)Unicode在之前加了兩個(gè)字節(jié)FE FF,之后則每個(gè)字符兩字節(jié)");
- }
- /**
- * 將該數(shù)組轉(zhuǎn)的每個(gè)byte轉(zhuǎn)為兩位的16進(jìn)制字符,中間用空格隔開(kāi)
- *
- * @param bytes
- * 要轉(zhuǎn)換的byte序列
- * @return 轉(zhuǎn)換后的字符串
- */
- public static final String bytesToHexString(byte[] bytes) {
- StringBuilder sb = new StringBuilder(bytes.length * 2);
- for (int i = 0; i < bytes.length; i++) {
- String hex = Integer.toHexString(bytes[i] & 0xff);// &0xff是byte小于0時(shí)會(huì)高位補(bǔ)1,要改回0
- if (hex.length() == 1)
- sb.append('0');
- sb.append(hex);
- sb.append(" ");
- }
- return sb.toString().toUpperCase();
- }
- }
原文鏈接:http://cherishlc.iteye.com/blog/1343502
【編輯推薦】