序)不少時候其實問題很簡單,問題在於本身懂得過於膚淺java
項目中須要用到一個功能,機器人模擬和人類聊天,玩家說出一句話以後,機器人本能的和他開始聊天,這破B玩意兒我以爲只要有強大的詞庫和拆分算法,就那麼點東西,可是要本身作還真是壓力滿滿的。因而果斷的在網上搜索,輕鬆的找到了這個東西:web
這玩意兒給個人第一感受就是實在,能夠,徹底可以知足需求,不過貌似它沒有提供接口,這不是事兒,果斷的翻網頁源碼,找到post的地方,是一個ajax的post請求,帶了一個參數,很簡單,因而果斷的來個java模擬HTTP,分分鐘搞定,寫了個junit測試下:ajax
10分鐘就搞定了一個智能聊天機器人,原本覺得問題就解決了,因而輕鬆的部署到jetty去,鄙人對jetty緋聞挺多了,websocket中的EOF曾經就搞了好久,部署上去以後,果斷開始使用安卓端測試下,客戶端發了一個:你好,另外一個客戶端收到:騫茶帿瀛�算法
明顯是亂碼了,感受debug一下:windows
能夠看到接受到http的返回值的時候已經亂碼了,在此過程當中經歷過以下步驟:數組
發出http請求 ---> 第三方接口http返回值 --> 本地jetty容器接受二進制流 --> jetty根據容器編碼從新編碼二進制數據 --> Java InputStream接受jetty編碼以後的流 --> java將其轉換成String服務器
因而咱們看到了一個String的返回值,而這個返回值亂碼,通常在漢字亂碼中分爲兩種狀況:websocket
1:騫茶帿瀛� 這樣的亂碼其實不叫亂碼,而是數據不是咱們想要的,由於咱們要的是A卻顯示成了B,這樣的緣由主要是由於編碼格式不正確致使eclipse
2:????? 全是問號的亂碼應該不少人都碰見過,這樣的東西應該纔是算亂碼,爲何會出現?。由於字節內的東西沒法用一個漢字展現出來,也就是找不到漢字對應這個內容,因而這樣的東西會以?的形式展現出來,官方的稱呼就是編碼黑洞,對應的二進制數據爲63,轉換後就是一個?socket
根據狀況來看本身遇到的是第一種,因而有點疑惑,管他的,來個強轉:
ChangeCharset changeCharset = new ChangeCharset(); try { result = changeCharset.toUTF_8(URLDecoder.decode(result, "UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result;
管你什麼編碼,哥給你來個統一,因而美滋滋的再次打開客戶端測試,又出現了另一種狀況:
機器人說:你好愛你哦親??
媽了個巴子,竟然有部分亂碼,因而繼續測試想找出規律,後來果真發現規律,只要過來的數據是偶數個,則不會亂碼,如果奇數個,則最後一個漢字亂碼,亂碼的形式是固定的?,來了一個?,我靠,今天兩種狀況都遇到了,本覺得很簡單的東西竟然卡在了編碼的地方,苦思冥想,很明顯是容器編碼問題,很SB的解決方法也很簡單,判斷下是否是奇偶,不是偶數加個東西就好了。
可是想搞明白爲何是最後一個字亂碼,忽然想到一個東西,UTF-8中,一個漢字3個字節,GBK中一個漢字2個字節,我好像明白了什麼。。
由於jetty容器默認是按照系統編碼來決定容器編碼,前提是沒有本身修改啓動編碼,而公司裏我臺PC是windows的,好像默認GBK的,反正我對windows緋聞也挺多的,因而這裏有一個問題,好比jetty接受到了一串通過UTF-8編碼的漢字:
我很好
jetty收到的最原始的二進制數組是這樣的:
[-26, -120, -111, -27, -66, -120, -27, -91, -67]
固然這不是最原始的,最原始的0和1,固然爲了好看就算他是最原始的吧,下一步jetty要開始編碼了,按照jetty的GBK編碼,他按照2個字節一個漢字的格式去編碼,因而出現了這樣的組合:
[-26, -120] [ -111, -27] [-66, -120] [-27, -91] [-67]
前面每兩個字節都能找到對應的漢字,最後jetty發現最後竟然只有一個字節,找不到對應的漢字,內心想這SB是哪來的,因而jetty放棄它了,把它趕出去,把63丟過去,因而最後的組合成了:
[-26, -120] [ -111, -27] [-66, -120] [-27, -91] [63]
通過GBK的格式編碼,兩個字節對應一個漢字,就顯示出了這樣的東西:
騫茶帿瀛?
會出現5個,由於每2個字節表明一個漢字,最後一個字節是63,對應的符號是?,就出現了上面的東西,因而我對它作了強制的UTF-8編碼,致使上面的二進制數組從新組合,通過UTF-8的組合以後,二進制數組成了這樣:
[-26, -120, -111] [-27, -66, -120] [-27, -91, 63]
再通過UTF-8顯示以後,變成了這樣:
我很�?
前6個字節可以正常的顯示出漢字,由於那就是真正的數據,然而最後3個字節,已經被GBK處理了,替換過了,即便使用UTF-8也沒法還原它原來的容貌,因而它就顯示成了上面的樣子,可是爲何偶數不會出錯?
由於偶數可以被GBK正常的解碼,也就是若是漢字是偶數,UTF-8和GBK是等同的,可是若是是奇數,則就出問題了,這也是傳說中的最後一個漢字亂碼的問題,由於最後一個 字節始終是63,要解決這個問題,必需要治標還要治本,項目中必須全程保證編碼一致性,由於我這個項目是遊戲服務器,走的WebSocket,要是Servlet能夠直接在Servlet裏面處理或者Filter處理。
不一樣的編碼方式,處理邏輯不同,不少時候咱們強轉看似解決了問題,其實只是問題沒有暴漏出來,知道其根本,方能指揮若定之中,決勝千里以外。
扯點題外話,前幾天半夜不知道抽了什麼風,將Centos升級到了6.6,結果在6.6下eclipse所有打不開,全是fatal級別的錯誤,反正不是我等閒人能解決的,那一次更新下載的更新包是1.4GB,明顯是系統兼容性的問題,我了個擦,因而想到一代碼農不能死於eclipse下,因而折騰下了VIM,弄了幾個插件。雖然之前也用VIM,可是沒有此次這麼正式,弄了下發現其實這玩意兒至關優秀,基本除了eclipse的打斷點Debug,其餘和他差很少,寫C/C++更快,一個\im過去整個文件註釋main總體就起來了,至關便捷,另外Linux下的UI也至關不錯,特別是DUCK桌面,和Mac基本差很少,甚至你能夠用java寫出這樣的東西:
對於程序猿,Linux是個好東西,有喜歡試水的朋友,能夠試試。
附上最近給本身系統加了DUCK美容後的美照:
結語)其實這個問題很簡單,只是當時太SB,想一想就揪心。。