【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

发现问题

事情是这样,最近在做一个微信支付对账功能,需要解析从微信下载回来的csv文件,发现解析出来交易时间为空。
去debug发现了一个很有意思的现象,上图:
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

出现的问题就是,同一个map,同样是全中文的key(为啥是中文key,因为微信返回来的账单就是中文),读取【交易时间】为空,读取【商户号】正常。so,why?

找到直接原因

我一顿操作猛如虎,找jdk源码debug了半天,一度怀疑是HashMap扩容导致了数据下标变了,然而实际上并非如此。。。。。。

后来无意中看到了一个被我忽略的细节,瞬间明朗。
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值
其实问题就在于,map里面的key实际上是[\uFEFF交易时间],只不过\uFEFF这个字符显示不出来,导致我误认为是[交易时间],当我通过get(交易时间)去取值,当然拿不到,因为真正的key==[\uFEFF交易时间],key!=[交易时间]。
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

分析根本原因

好了,直接原因找到了,那继续深入排查根本原因。这个\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标记。
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

解决方案

1、另存为一份无签名的UTF-8格式的csv

我们可以用第三方编辑器另存为一份无签名的UTF-8格式的csv,再试一下
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

2、去掉BOM头再读取

由于文件是自动从微信下载,不可能手工另存为,所以只能每次先去掉BOM头再读取数据。
由于我使用了hutool工具包,hutool封装了一个BOMInputStream,可以解决这个问题,所以我这里直接使用hutool工具处理。
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值
这里注意一定要调用getCharset()方法才会自动去掉BOM头
【眼见不一定为实】HashMap的key和value明明存在,但是获取不到值

上一篇:在VS项目中通过GIT生成版本号作为编译版本号


下一篇:DOM与BOM