背景
接上一篇《安装配置Sftp并通过java访问》,由于我们上传的文件是通过程序生成标准的文件csv格式文件,而乙方是通过人肉的方式把外呼结果汇总之后创建txt文件然后修改后缀的方式变成csv文件,这样会导致我们程序解析的时候遇到一些问题,比如bom文件头问题(他们是windows系统,只有windows系统把txt改成csv会出现bom头问题),导致我们程序解析出错,当然我们作为一个有品德有追求的程序员,肯定不会学他们通过有功的方式去解析,那么接下来就通过程序兼容的方式,解析带bom头的csv文件。
解析兼容
引入依赖
代码语言:javascript复制<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
</dependency>
1.常规csv文件解析
代码语言:javascript复制List<T> resultList = new ArrayList<>();
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
ByteArrayInputStream byteArrayInputStream = null;
CSVParser parser = null;
try {
byteArrayInputStream = new ByteArrayInputStream(bytes);
inputStreamReader = new InputStreamReader(byteArrayInputStream);
//reader = new UnicodeReader(byteArrayInputStream,"utf-8");
//bufferedReader = new BufferedReader(reader);
bufferedReader = new BufferedReader(inputStreamReader);
parser = CSVFormat.DEFAULT
.withHeader("a","b")
.withFirstRecordAsHeader()
.parse(bufferedReader);
//int rowIndex = 0;
for (CSVRecord record : parser.getRecords()) {
//transfer record to row
T row = ...
log.info("read data from ftp;row={}",row);
resultList.add(row);
}
} catch (UnsupportedEncodingException e) {
log.error("occur error;filePath={}",filePath,e);
} catch (IOException e) {
log.error("occur error;filePath={}",filePath,e);
} catch (Exception e) {
log.error("occur error;filePath={}",filePath,e);
} finally {
IOUtils.closeQuietly(byteArrayInputStream);
IOUtils.closeQuietly(inputStreamReader);
//IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(bufferedReader);
IOUtils.closeQuietly(parser);
}
这种情况下解析常规的csv文件没有任何问题,但是带bom头的文件解析不了。原因是csv理论上也是纯文本文件,不排除认为的生成txt文件通过改后缀名的方式变成了csv文件,或者在windows平台手动生成的csv,都是带bom头的,用命令打开文件的时候会发现文件头部有乱码现象。
2.使用bom流解析兼容
代码语言:javascript复制List<T> resultList = new ArrayList<>();
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
ByteArrayInputStream byteArrayInputStream = null;
BOMInputStream bomInputStream = null;
CSVParser parser = null;
try {
byteArrayInputStream = new ByteArrayInputStream(bytes);
//使用BOMInputStream兼容bom头csv文件
bomInputStream = new BOMInputStream(byteArrayInputStream,false, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE,ByteOrderMark.UTF_8);
String charset = "UTF-8";
if(bomInputStream.hasBOM()) {
charset = bomInputStream.getBOMCharsetName();
}
inputStreamReader = new InputStreamReader(bomInputStream, Charset.forName(charset));
//reader = new UnicodeReader(byteArrayInputStream,"utf-8");
//bufferedReader = new BufferedReader(reader);
bufferedReader = new BufferedReader(inputStreamReader);
parser = CSVFormat.DEFAULT
.withHeader("a","b")
.withFirstRecordAsHeader()
.parse(bufferedReader);
//int rowIndex = 0;
for (CSVRecord record : parser.getRecords()) {
T row = ...
log.info("read data from ftp;row={}",row);
resultList.add(row);
}
} catch (UnsupportedEncodingException e) {
log.error("occur error;filePath={}",filePath,e);
} catch (IOException e) {
log.error("occur error;filePath={}",filePath,e);
} catch (Exception e) {
log.error("occur error;filePath={}",filePath,e);
} finally {
IOUtils.closeQuietly(byteArrayInputStream);
IOUtils.closeQuietly(bomInputStream);
IOUtils.closeQuietly(inputStreamReader);
IOUtils.closeQuietly(bufferedReader);
IOUtils.closeQuietly(parser);
}
原理是bom流能检测到bom头,且在流中exclude掉bom。
3.使用UnicodeReader解析兼容
类似以上代码:
代码语言:javascript复制UnicodeReader ur = new UnicodeReader(fis, "utf-8");
bufferedReader = new BufferedReader(ur);
UnicodeReader 通过PushbackInputStream InputStreamReader实现BOM的自动检测和过滤读取;当没有检测到BOM时,pushback流将回退,并采用构造函数传入的编码进行读取。否则使用BOM对应的编码进行读取。
总结
对于上一节的2和3,相对来说,3方式更加轻量和强大;另外也更加透明,可以随便修改源码来实现自己的需求。
UnicodeReader参考:http://akini.mbnet.fi/java/unicodereader/