讀取含有BOM頭的文件遇到的問題


需求是讀取一個csv文件,然后解析成對應的數據結構。csv必須包含指定的某些列,通過列名header來進行校驗。

 

解析配置文件的方法。

 1 public List<QuestionData> buildConfigData(final MultipartFile file) {
 2 
 3     CsvReader csvReader = null;
 4     List<QuestionData> questionDataList;
 5     try (DataInputStream inputStream = new DataInputStream(file.getInputStream())) {
 6         csvReader = new CsvReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
 7 
 8         if (!csvReader.readHeaders()) {
 9             return Lists.newLinkedList();
10         }
11 
12         final String[] headers = csvReader.getHeaders();
13         getAndCheckHeader(headers);
14         questionDataList = getQuestionData(csvReader, headers);
15 
16     } catch (final IOException e) {
17         log.error("解析配置文件錯誤", e);
18         throw new FatalException("解析配置文件錯誤");
19     } finally {
20         if (csvReader != null) {
21             csvReader.close();
22         }
23     }
24     return questionDataList;
25 }

 

其中,檢查header的方法:

private static final Set<String> NEEDED_COLUMNS = ImmutableSet
        .of(QuestionDataType.ORDER.name(), QuestionDataType.DESC.name(), QuestionDataType.OPTION_A.name(),
                QuestionDataType.OPTION_B.name(), QuestionDataType.OPTION_C.name(), QuestionDataType.ANSWER.name());

private void getAndCheckHeader(final String[] headers) {
    //某些必要的列不存在
    HashSet<String> sets = Sets.newHashSet(headers);
    if (!sets.containsAll(NEEDED_COLUMNS)) {
        throw new FatalException("缺少必要的列信息");
    }
}

 

實際出現的問題是,上傳文件的時候總是出現缺少必要的列信息這個異常。debug發現,containsAll這個方法一直返回false,但是看NEEDED_COLUMNS里面的字符串,在header里面都存在,例如ORDER字符串:

 

從這里看,headers里面有ORDER字符串,但是NEEDED_COLUMNS.contains(headers[0])返回的結果就是false。

 

debug時使用evaluate,將headers[0]的value  copy一下,粘貼到輸入框里,就發現了問題:

 

 

可以看的出來,headers[0]的實際值是"\uFEFFORDER",而非"ORDER",前面多了一個"\uFEFF"。

經查,"\uFEFF"是BOM頭,windows下保存文件時經常會插入在字符串最前面,debug時直接看值是看不出來有這個BOM頭的。

 

解決方案,使用apache的BOMInputStream,可以過濾掉BOM頭:

public List<QuestionData> buildConfigData(final MultipartFile file) {

    CsvReader csvReader = null;
    List<QuestionData> questionDataList;

    //過濾BOM頭
    try (BOMInputStream inputStream =  new BOMInputStream(file.getInputStream())) {
        csvReader = new CsvReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));

        if (!csvReader.readHeaders()) {
            return Lists.newLinkedList();
        }

        final String[] headers = csvReader.getHeaders();
        getAndCheckHeader(headers);
        questionDataList = getQuestionData(csvReader, headers);

    } catch (final IOException e) {
        log.error("解析配置文件錯誤", e);
        throw new FatalException("解析配置文件錯誤");
    } finally {
        if (csvReader != null) {
            csvReader.close();
        }
    }
    return questionDataList;
}

使用BOMInputStream,將原有的InputSteam包一層即可。

 

參考文章:Java處理文件BOM頭的方式推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM