發現問題
事情是這樣,最近在做一個微信支付對賬功能,需要解析從微信下載回來的csv文件,發現解析出來交易時間為空。
去debug發現了一個很有意思的現象,上圖:
出現的問題就是,同一個map,同樣是全中文的key(為啥是中文key,因為微信返回來的賬單就是中文),讀取【交易時間】為空,讀取【商戶號】正常。so,why?
找到直接原因
我一頓操作猛如虎,找jdk源碼debug了半天,一度懷疑是HashMap擴容導致了數據下標變了,然而實際上並非如此。。。。。。
后來無意中看到了一個被我忽略的細節,瞬間明朗。
其實問題就在於,map里面的key實際上是[\uFEFF交易時間],只不過\uFEFF這個字符顯示不出來,導致我誤認為是[交易時間],當我通過get(交易時間)去取值,當然拿不到,因為真正的key==[\uFEFF交易時間],key!=[交易時間]。
分析根本原因
好了,直接原因找到了,那繼續深入排查根本原因。這個\uFEFF到底是個啥???叫做BOM。
BOM(Byte Order Mark),字節順序標記,出現在文本文件頭部,Unicode編碼標准中用於標識文件是采用哪種格式的編碼,但它對於文件的讀者來說是不可見字符。
BOM定義:http://www.unicode.org/unicode/faq/utf_bom.html
因為Unicode可以采用16位或者32位編碼,所以計算機在處理時需要知道其字節順序,BOM就是用來標識字節流的字節順序的,但字節順序這個 概念對UTF-8來說是沒有意義的,所以BOM對UTF-8同樣沒有意義。但Unicode標准BOM在UTF-8編碼格式中存在,其存在位置在文件開 頭,以三個字節0xEF, 0xBB, 0xBF表示。
UTF-8編碼不推薦使用無意義的BOM,但許多Windows程序卻在保存UTF-8編碼的文件時將其存為帶BOM的格式(即在文件開頭加上0xEFBBBF三個字節),這么干的就包括Windows記事本。
因此,在編輯UTF-8的文件時,建議不要使用記事本等進行編輯,雖然保存后的文件仍然是UTF-8,但卻已經不再是保存前的UTF-8了。
因為BOM存在於文件開頭,而【交易時間】剛好就是第一個被讀取的csv列,所以被讀取到一塊去了,導致了前面的此【交易時間】非彼【交易時間】。
用文本編輯器打開csv,其實也是看不到這個編碼的,但我們可以發現這個csv文件的編碼格式為【UTF-8 有簽名】,也就是帶了BOM標記。
解決方案
1、另存為一份無簽名的UTF-8格式的csv
我們可以用第三方編輯器另存為一份無簽名的UTF-8格式的csv,再試一下
2、去掉BOM頭再讀取
由於文件是自動從微信下載,不可能手工另存為,所以只能每次先去掉BOM頭再讀取數據。
由於我使用了hutool工具包,hutool封裝了一個BOMInputStream,可以解決這個問題,所以我這里直接使用hutool工具處理。
這里注意一定要調用getCharset()方法才會自動去掉BOM頭