2016-12-27
對字符編碼時的規則
通常如果一樣東西需要編碼,說明這樣東西並不適合傳輸。原因多種多樣,如Size過大,包含隱私數據。對於Url來說,之所以要進行編碼,一個是 因為Url中有些字符會引起歧義。例如Url參數字符串中使用key=value鍵值對這樣的形式來傳參,鍵值對之間以&符號分隔,如/s?q=abc&ie=utf-8。 如果你的value字符串中包含了=或者&,那么勢必會造成接收Url的服務器解析錯誤,因此必須將引起歧義的&和=符號進行轉義,也就是對其進行編碼。另外, Url的編碼格式采用的是ASCII碼,而不是Unicode(包含中文),這也就是說你不能在Url中包含任何非ASCII字符,例如中文。否則如果客戶端瀏覽器和服務端瀏覽器支持的字符集不同的情況下,中文可能會造成問題。
編碼時的規則:
- 【字母】字符 "a" 到 "z"、"A" 到 "Z" 和【數字】字符 "0" 到 "9" 保持不變
- 特殊字符【.】【-】【*】【_】保持不變
- 空格字符 【 】轉換為一個加號【+】
- 所有其他字符都是不安全的,因此首先使用一些編碼機制將它們轉換為【一個或多個】字節,然后每個字節用一個包含 3 個字符的字符串【%xy】表示,其中 xy 為該字節的兩位十六進制表示形式。
- 可以指定對這些字符進行解碼的編碼機制,或者如果未指定的話,則使用平台的默認編碼機制。
例如,使用 UTF-8 編碼機制,字符串【ü@】將轉換為【%C3%BC%40】,因為在 UTF-8 中,字符【ü】編碼為兩個字節【 0x C3和 0x BC】,字符【@】編碼為一個字節【0x40】。
解碼時的規則:
- 將把【%xy】格式的子序列視為【一個字節】,其中 xy 為 8 位的兩位十六進制表示形式。
- 然后,所有連續包含一個或多個這些字節序列的子字符串,將被其編碼可生成這些連續字節的字符所代替。
測試代碼
public class Test {public static void main(String[] args) throws Exception {String enc = "UTF-8";test("aA0 .-*_", enc);//編碼【aA0+.-*_】解碼【aA0 .-*_】test("@", enc);//編碼【%40】解碼【@】test("ü", enc);//編碼【%C3%BC】解碼【ü】test("白", enc);//編碼【%E7%99%BD】解碼【白】test("白", "GBK");//編碼【%B0%D7】解碼【白】}/*** 測試編解碼* @param str 原始字符串* @param enc 對字符串進行URLEncoder和URLDecoder時使用的編碼規則* @throws UnsupportedEncodingException*/public static void test(String str, String enc) throws UnsupportedEncodingException {String encode = URLEncoder.encode(str, enc);//推薦 UTF-8。如果未指定,則使用平台的默認編碼String decode = URLDecoder.decode(encode, enc);//編碼和解碼必須使用同一套碼表,否則解碼時可能亂碼System.out.println("原始字符串【" + str + "】編碼后【" + encode + "】解碼后【" + decode + "】");//獲取通過UTF8碼表編碼的字符串的字節數組byte[] bytes = str.getBytes(enc);System.out.println(" 原始字符串對應的字節數組" + Arrays.toString(bytes));StringBuilder sb = new StringBuilder();for (byte b : bytes) {//手動對【每個字節】都進行上面【類似URLEncoder】編碼規則的算法進行編碼(即使字母和數字也進行了編碼)sb.append(Integer.toHexString(b & 0xFF).toUpperCase() + " ");}System.out.println(" 對每個字節都進行編碼【" + sb.toString().trim() + "】");}
//源碼public static String decode(String s, String enc) throws UnsupportedEncodingException {boolean needToChange = false;int numChars = s.length();//字符數StringBuffer sb = new StringBuffer(numChars > 500 ? numChars / 2 : numChars);int i = 0;if (enc.length() == 0) throw new UnsupportedEncodingException("URLDecoder: empty string enc parameter");char c;byte[] bytes = null;while (i < numChars) {c = s.charAt(i);//逐個遍歷所有字符switch (c) {case '+':sb.append(' ');i++;needToChange = true;break;case '%':try {if (bytes == null) bytes = new byte[(numChars - i) / 3];// (numChars-i)/3 is an upper bound上線 for the number of remaining剩下的 bytesint pos = 0;while (((i + 2) < numChars) && (c == '%')) {int v = Integer.parseInt(s.substring(i + 1, i + 3), 16);//16進制if (v < 0) throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - negative value");bytes[pos++] = (byte) v;i += 3;if (i < numChars) c = s.charAt(i);}// A trailing, incomplete不完全的 byte encoding such as "%x" will cause an exception to be thrownif ((i < numChars) && (c == '%')) throw new IllegalArgumentException("URLDecoder: Incomplete trailing escape (%) pattern");sb.append(new String(bytes, 0, pos, enc));} catch (NumberFormatException e) {throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - " + e.getMessage());}needToChange = true;break;default:sb.append(c);i++;break;}}return (needToChange ? sb.toString() : s);}}