这次是一个需求,系统需要支持中文的用户pin。
优惠券发放任务创建的时候可以上传txt文件到jss,
发放任务是一个定时任务,每隔三分钟遍历一边待发放任务,从jss上下载pin文件,遍历组装成一个pin的集合,然后根据pin集合发放优惠券。
因此中文编码问题主要出现在从jss上获取输入流读取数据,并正确解析,转成utf-8格式
txt主要有ANSI、UTF-8、Unicode编码
因此首先需要判断这个文件是什么编码格式的,才能正确解析。
附上判断编码格式代码:
private static String getFilecharset(BufferedInputStream bis) throws IOException {
String charset = "GBK";
byte[] first3Bytes = new byte[3];
boolean checked = false;
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1) {
return charset; //文件编码为 ANSI
} else if (first3Bytes[0] == (byte) 0xFF
&& first3Bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE"; //文件编码为 Unicode
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE
&& first3Bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE"; //文件编码为 Unicode big endian
checked = true;
} else if (first3Bytes[0] == (byte) 0xEF
&& first3Bytes[1] == (byte) 0xBB
&& first3Bytes[2] == (byte) 0xBF) {
charset = "UTF-8"; //文件编码为 UTF-8
checked = true;
}
bis.reset();
if (!checked) {
int loc = 0;
while ((read = bis.read()) != -1) {
loc++;
if (read >= 0xF0)
break;
if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
break;
if (0xC0 <= read && read <= 0xDF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF) (0x80 - 0xBF),也可能在GB编码内
continue;
else{
break;
}
} else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是几率较小
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
charset = "UTF-8";
break;
} else{
break;
}
} else{
break;
}
}
}
}
return charset;
}
获取到编码格式之后,我们就可以使用这个编码格式读取数据,并且转换成UTF-8格式
如下;
decodein = os.get().getInputStream();
in = os.get().getInputStream();
String oldCharset = getFilecharset(new BufferedInputStream(decodein));
reader = new BufferedReader(new InputStreamReader(in,oldCharset));
String pin = null;
while ((pin = (reader.readLine())) != null) {
pins.add(pin);
}
为何出现了读取数据为空的情况?
附上产生这个bug的代码;
in = os.get().getInputStream();
//检验编码格式
String oldCharset = getFilecharset(new BufferedInputStream(in));
reader = new BufferedReader(new InputStreamReader(in,oldCharset));
while ((pin = (reader.readLine())) != null) {
pins.add(pin);
}
这个问题排查了两天,从各种编码问题到jss问题
结果竟然是很简单的原因: inputStream调用了两次
原本我以为使用BufferedInputStream、InputStreamReader封装了inputStream之后就是新的一个流了,毕竟使用了new 。结果发现其实都是封装了同一个inputStream。
这个问题其实很简单,但是很容易被忽视。