解析csv文件兼容bom头

2021-12-21 19:50:24 浏览数 (1)

背景

接上一篇《安装配置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/

0 人点赞