1. 亂碼出現的原因是什麼?
出現亂碼的根本原因是用戶端、服務端兩端編碼格式不一緻導緻的。
2. 兩端的編碼格式一般是什麼?
用戶端:多數情況下,用戶端的編碼格式是 UTF-8。
服務端:服務端會根據不同的請求方法使用不同的編碼格式。如:請求方法為 POST 時,編碼格式為 UTF-8;請求方法為 GET 時,編碼格式為 ISO8859-1。
3. 如何解決亂碼問題?
當請求方法為 POST 時,用戶端和服務端兩邊的編碼格式一緻,是以不存在亂碼問題。是以此處着重看下如何解決當請求方法為 GET 時的亂碼問題。
解決方法倒也簡單,隻不過需要用戶端和服務端配合:
3.1 用戶端需要做什麼?
在向 URL 添加參數之前,先對目标參數進行兩次 encode,如 UTF-8:
String username = "xxx";
username = URLEncoder.encode(username,"UTF-8");
username = URLEncoder.encode(username,"UTF-8");
String url = xxxxx.xxx + "?username=" + username + "&password=" + password;
3.2 服務端需要做什麼?
伺服器在收到資料之後,隻需将資料進行一次跟用戶端編碼格式一樣的 decode,如 UTF-8:
String username = URLDecoder.decode(username, "UTF-8")
這樣處理之後,兩邊就不會再出現亂碼了。
4. 為什麼對 URL 中的參數進行兩次 encode 之後,就可以解決亂碼問題了?
4.1 原了解析
通過上面的分析可知,亂碼産生的主要原因是用戶端、伺服器兩邊編碼不一緻造成的,即發送 GET 請求時,用戶端使用的是 UTF-8 編碼格式對 URL 中的參數進行編碼,而伺服器在接收資料的時候,使用的是 ISO8859-1(解析 POST 請求時,伺服器使用的編碼格式是 UTF-8 編碼格式)編碼格式對 URL 中的參數進行解碼。
ISO8859-1 跟 ASCII 碼一樣,都是單位元組編碼,ISO8859-1 是從 ASCII 擴充而來的。ISO8859-1 将 ASCII 一個位元組中剩餘的最後一位用了起來,也就是說,它比 ASCII 多了 128 個字元。另外,因為 ISO8859-1 是從 ASCII 擴充而來的,是以,ISO8859-1 相容 ASCII。
原資料:
極速
用戶端第一次編碼,URLDecoder.decode(username, “UTF-8”) 編碼之後:
%E6%9E%81%E9%80%9F
用戶端第二次編碼,URLDecoder.decode(username, “UTF-8”) 編碼之後:
%25E6%259E%2581%25E9%2580%259F
用戶端發出的 URL:
http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456
伺服器接收的 URL:
http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456
伺服器第一次解碼,伺服器接收到 GET 請求之後,預設會用 ISO8859-1 編碼格式解碼,解碼之後得到:
http://192.168.31.148:8080/OkHttpServer/login?username=%E6%9E%81%E9%80%9F&password=123456
需要注意的是,伺服器用 ISO8859-1 編碼格式解碼 URL 中的參數是自動完成的。
因為用戶端第一次用 URLDecoder.decode(username, “UTF-8”) 編碼 URL 中參數之後,得到的是 ASCII 碼,且 UTF-8 和 ISO8859-1 對 ASCII 的編碼結果是一緻的,是以,用戶端第二次用 URLDecoder.decode(username, “UTF-8”) 之後的結果可以直接用 ISO8859-1 編碼格式解碼。
由于伺服器解碼之後的 URL 中的參數是用 UTF-8 編碼格式編碼的,是以,此時需要伺服器再用 UTF-8 編碼格式解碼一次。
伺服器第二次解碼,伺服器用 UTF-8 編碼格式解碼之後得到:
http://192.168.31.148:8080/OkHttpServer/login?username=極速&password=123456
4.2 實際應用
如果用戶端程式員沒有顯式用 UTF-8 編碼格式編碼 URL 中的參數,服務端要如何處理才能擷取到原資料?
首先,分析下如果用戶端沒有用 UTF-8 編碼格式編碼 URL 中的參數,程式是如何執行的:
網絡請求架構會對 URL 中的參數進行一次 UTF-8 編碼:
URLDecoder.encode(username, "UTF-8")
伺服器會對 URL 中的參數進行一次 ISO8859-1 編碼:
URLDecoder.decode(username, "ISO8859-1")
明白了執行流程之後,如何解決自然也就顯而易見了:
先轉回 ISO8859-1 解碼(decode)之前的結果,再轉會 UTF-8 編碼(encode)之前的結果。
具體操作步驟:
//1. 先轉回 ISO8859-1 解碼(decode)之前的結果
String temp = URLDecoder.encode(username, "ISO8859-1");
//2. 再轉會 UTF-8 編碼(encode)之前的結果
temp = URLDecoder.decode(username, "UTF-8")
4.3 為什麼 URL 中的參數經 UTF-8 編碼格式編碼之後不能通過 ISO8859-1 編碼格式直接解碼呢?
因為 URL 中的參數經 UTF-8 編碼格式編碼之後得到的結果在 ISO8859-1 字元集可能一樣也可能根本表示不了,這也是為什麼 ASCII 碼經 UTF-8 編碼格式編碼之後的結果可以用 ISO8859-1 編碼格式解碼。如,在 Unicode 字元集中,第 20013 個字元是“中”,而在 ISO8859-1 字元集中,一共才有 256 個字元。字元“中”經 UTF-8 編碼之後的結果再經 ISO8859-1 解碼,無論如何也得不到正确答案的。