项目场景:
项目是一个读取银行流水文件来分析流水真实性及根据流水构造客户画像,生成尽调报告等
问题描述
项目里 对csv格式的文件读取分析存在问题,排查一遍之后,发现是 同事用 io流重复使用导致,熟悉Java的人可能都知道,Java中的Inputstream是不能重复读取的 :
/**
* 读取 csv 文件
*/
private void dealCSV(InputStream in, List<List<String>> list) throws UnsupportedEncodingException {
boolean flag = false;
boolean utf_flag_16 = false;
CSVReader csvReader = new CSVReader(new InputStreamReader(in, "gbk"));
for (String[] row : csvReader) {
if (!flag) {
for (String s : row) {
// 如果通篇没有这两个字,则判断该文件读取乱码
if (s.contains("交易") || s.contains("额")) {
flag = true;
break;
}
}
}
list.add(Arrays.asList(row));
}
// 文件读取乱码,采用 utf-8 读取
if (!flag) {
list.clear();
csvReader = new CSVReader(new InputStreamReader(in, StandardCharsets.UTF_8));
for (String[] row : csvReader) {
List<String> items = new ArrayList<>(Arrays.asList(row));
if (!utf_flag_16) {
for (String s : row) {
// 如果通篇没有这两个字,则判断该文件读取乱码
if (s.contains("交易") || s.contains("额")) {
utf_flag_16 = true;
break;
}
}
}
list.add(items);
}
// 2023.8.2新增 utf-16格式,有些csv格式转换utf-8也是乱码
setUtf16(in, utf_flag_16, list, true);
}
// 特殊字符处理
for (List<String> item : list) {
if (CollectionUtils.isNotEmpty(item)) {
int size = item.size();
for (int i = 0; i < size; i++) {
String str = item.get(i);
if (StringUtils.isNotBlank(str)) {
str = str.replace("\uFEFF", "");
item.set(i, str);
}
}
}
}
}
原因分析:
分析: 两次使用了 InputStream in 流,在InputStream读取的时候,会有一个pos指针,他指示每次读取之后下一次要读取的起始位置,当读到最后一个字符的时候,pos指针不会重置,导致 第一次读取如果是格式不对导致乱码,第二次使用 UTF-8时使用的 InputStream 已经读到最后了,不会再读取出什么东西,导致后面的代码失效
解决方案:
具体解决方案:明确了问题,解决方式就很简单了
.采用可以重复使用的 流
private ByteArrayOutputStream getBOS(InputStream in) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
BufferedInputStream br = new BufferedInputStream(in);
for(int c=0;(c=br.read())!=-1;){
bos.write(c);
}
br.close();
} catch (Exception e) {
logger.error("",e);
}
return bos;
}
这个时候的bis是可以被多次重复读取,close对其无效,但是要注意每次读前,调用bis.reset();方法需要将游标重置到流的头部。
InputStream bis = new ByteArrayInputStream(bos.toByteArray());