spring boot项目中,有些配置,我不愿意正正经经,规规矩矩地写一个配置实体类来读取,那样要写不少代码,颇费周折,就想简单地通过文件读取的方式,将内容读进来,然后解释、处理。于是用记事本创建了一个.txt文件。内容有汉字,读取出来之后,做一些查找、匹配操作(indexOf()),结果失败。
究其原因,首先我读取文件的时候,用的是“ISO-8859-1”编码,然后项目默认编码是“UTF-8”,所以读取出来,当然比较失败;但将读取内容转化成“UTF-8”时,第一行的结果仍然比较失败,而后面的则正常。最后发现,该配置文件的编码方式为"UTF-8 BOM",文件开头多了3个特殊字节,所以第一行就比较失败了。
UTF-8 BOM其实是一种特殊的UTF-8,跟UTF-8编码文件的唯一不同,是在文件开头加入了一个3字节的特殊标记,用于指定每个字对应的数据在内存地址中存放顺序,即俗称的大端小端。大端模式,数据的高字节保存在内存的低地址,而数据的低字节保存在内存的高地址中。小端模式则相反。但是,无BOM(byte-order mark,字节顺序)的UTF-8才是标准的,UTF-8里塞入一个BOM只是微软的习惯用法。也因此,这个文本文件qj.txt我在intellij idea里编辑,是纯的utf-8编码;用记事本打开,修改了一下,就变成utf-8 bom编码了。
由于BOM只在文件开头,所以第一行数据比较失败,后面的则正常。
读取代码如下:
//读取文件内容,返回由行组成的数组
private ArrayList _getAllQj(){
ArrayList lines = new ArrayList();
try{
String path = ResourceUtils.getURL("classpath:").getPath();
path = URLDecoder.decode(path, "utf-8");
path = String.format("%s\\qj.txt",path);
/*
RandomAccessFile(),默认编码是用"ISO-8859-1"
*/
RandomAccessFile raf = new RandomAccessFile(path, "r");//只读方式读文件
String line = raf.readLine();
while(line != null){
lines.add(line);
line = raf.readLine();
}
raf.close();
} catch(Exception ex){
}
return lines;
}
//比较与匹配
public String getQj(String name) {
String url = "";
ArrayList lines = _getAllQj();//读取文件内容,返回由行组成的数组
for(Object obj : lines){
String line = obj.toString();
String[] values = line.split(",");//侠客岛,http://localhost/quanjing/侠客岛全景/view.htm
try {
/*
name2,表面上看值为“侠客岛”,但它前面多出了3个字节!导致后面的indexOf()失败
*/
String name2 = new String(values[0].getBytes("ISO-8859-1"),"UTF-8");
//name = “侠客岛管理委员会”
if(name.indexOf(name2) >= 0) {//结果是false!
url = values[1];
break;
}
} catch(Exception ex){
}
}
return (url.length() > 0) ? "redirect:" + url : url;
}
知道了问题所在就可以想好如何应对。可以将文件转回标准的utf-8编码,也可以在第一行写上不参与比较的注释,最好用英文,以避开这个雷区。
每当代码中需要用汉字来做配置或比较时,我都有点担心,担心在哪个环节不小心就会出问题。这编码那编码。这个问题,从20、30年前我接触电脑时就存在,到了今天,这个问题仍然是个坑,不小心就会掉进去。