繁体   English   中英

使用opencsv(java)读取.csv文件时跳过空行

[英]Skip blank lines while reading .csv file using opencsv (java)

大家好,我的目标是让 csv 阅读器在解析文件时跳过空白行,基本上什么都不做。 只获取至少有一个值的行,目前我有两种方法 -> 第一种只是将所有行读取为字符串列表数组并返回它,第二种将结果转换为字符串列表列表:两者如下所示:

private List<String[]> readCSVFile(File filename) throws IOException {

    CSVReader reader = new CSVReader(new FileReader(filename));
    List<String[]> allRows = reader.readAll();

    return allRows;

}

public List<List<String>> readFile(File filename) throws IOException {

        List<String[]> allRows = readCSVFile(filename);     
        List<List<String>> allRowsAsLists = new ArrayList<List<String>>();      
        for (String[] rowItemsArray :  allRows) {
            List<String> rowItems = new ArrayList<String>();
            rowItems.addAll(Arrays.asList(rowItemsArray));
            allRowsAsLists.add(rowItems);

        }
    return allRowsAsLists;

}

我的第一个想法是检查(在第二种方法中)数组的长度,如果它是 0 只是为了忽略它——这将是这样的:

for (String[] rowItemsArray :  allRows) {
            **if(rowItemArray.length == 0) continue;**
            List<String> rowItems = new ArrayList<String>();
            rowItems.addAll(Arrays.asList(rowItemsArray));
            allRowsAsLists.add(rowItems);

}  

不幸的是,这没有用,因为即使该行是空白的,它仍然返回一个元素数组——实际上是空字符串。 检查单个 String 不是一个选项,因为有 100 多个列并且这是可变的。 请建议实现此目标的最佳方法是什么。 谢谢。

整理出来是这样的:

    public List<List<String>> readFile(File filename) throws IOException {

            List<String[]> allRows = readCSVFile(filename, includeHeaders, trimWhitespacesInFieldValues);       
            List<List<String>> allRowsAsLists = new ArrayList<List<String>>();      
            for (String[] rowItemsArray :  allRows) {
                **if(allValuesInRowAreEmpty(rowItemsArray)) continue;**
                List<String> rowItems = new ArrayList<String>();
                rowItems.addAll(Arrays.asList(rowItemsArray));
                allRowsAsLists.add(rowItems);

            }
            return allRowsAsLists;

        }

    private boolean allValuesInRowAreEmpty(String[] row) {
        boolean returnValue = true;
        for (String s : row) {
            if (s.length() != 0) {
                returnValue = false;
            }
        }
        return returnValue;
    }

您可以检查长度和第一个元素。 如果该行仅包含字段分隔符,则长度 > 1。如果该行包含单个space字符,则第一个元素不为空。

if (rowItemsArray.length == 1 && rowItemsArray[0].isEmpty()) {
    continue;
}

对于 opencsv 5.0,有一个 API 选项可以将 CSV 行直接读入 Bean。

对于喜欢使用“CsvToBean”功能的人,以下解决方案是使用 CsvToBeanBuilder 上的(遗憾地已弃用)#withFilter(..) 方法来跳过 Inputstream 中的空行:

InputStream inputStream; // provided
List<MyBean> data = new CsvToBeanBuilder(new BufferedReader(new InputStreamReader(inputStream)))
    .withType(MyBean.class)
    .withFilter(new CsvToBeanFilter() {
        /*
         * This filter ignores empty lines from the input
         */
        @Override
        public boolean allowLine(String[] strings) {
            for (String one : strings) {
                if (one != null && one.length() > 0) {
                    return true;
                }
            }
            return false;
        }
    }).build().parse();

更新:随着 opencsv 5.1 版(日期为 2/2/2020), CsvToBeanFilter 根据功能请求#120不再推荐。

您可以使用带有 lambda: 的过滤器,如下所示:

CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(new StringReader(CSV_HEADER + "\n" + lines))
    .withType(clazz)
    .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
    .withSeparator(delimiter)
    .withSkipLines(skipLines)
    .withIgnoreLeadingWhiteSpace(true).withFilter(strings -> {
      for (String r : strings) {
        if (r != null && r.length() > 0) {
          return true;
        }
      }
      return false;
    }).build();

您的 lambda 过滤器:

.withFilter(strings -> {
      for (String r : strings) {
        if (r != null && r.length() > 0) {
          return true;
        }
      }
      return false;
    })

您可以在修剪后总结每行的所有字符串值。 如果结果字符串为空,则任何单元格中都没有值。 在这种情况下,请忽略该行。
像这样的东西:

private boolean onlyEmptyCells(ArrayList<String> check) {
    StringBuilder sb = new StringBuilder();
    for (String s : check) {
        sb.append(s.trim());
    }
    return sb.toString().isEmpty(); //<- ignore 'check' if this returns true
}

这是基于@Martin解决方案的 lambda 更新解决方案

InputStream inputStream; // provided
List<MyBean> data = new CsvToBeanBuilder(new BufferedReader(new InputStreamReader(inputStream)))
    .withType(MyBean.class)
    // This filter ignores empty lines from the input
    .withFilter(stringValues -> Arrays.stream(stringValues)
        .anyMatch(value -> value != null && value.length() > 0))
    .build()
    .parse();

如果您不解析为 Bean,则可以使用 Java Streams API 来帮助您过滤无效的 CSV 行。 我的方法是这样的(其中is带有 CSV 数据的java.io.InputStream实例,而YourBean map(String[] row)是将 CSV 行映射到 Java 对象的映射方法:

CSVParser csvp = new CSVParserBuilder()
    .withSeparator(';')
    .withFieldAsNull(CSVReaderNullFieldIndicator.BOTH)
    .build();
CSVReader csvr = new CSVReaderBuilder(new InputStreamReader(is))
    .withCSVParser(csvp)
    .build();
List<YourBean> result = StreamSupport.stream(csvr.spliterator(), false)
    .filter(Objects::nonNull)
    .filter(row -> row.length > 0)
    .map(row -> map(row))
    .collect(Collectors.toList());

CsvToBeanFilter的 JavaDoc 声明“这是一个示例,展示如何使用 CsvToBean 删除空行。因为解析器返回一个包含单个空字符串的数组,用于检查它的空行。” 并列出了如何执行此操作的示例:

private class EmptyLineFilter implements CsvToBeanFilter {

    private final MappingStrategy strategy;

    public EmptyLineFilter(MappingStrategy strategy) {
        this.strategy = strategy;
    }

    public boolean allowLine(String[] line) {
        boolean blankLine = line.length == 1 && line[0].isEmpty();
        return !blankLine;
    }

 }

 public List<Feature> parseCsv(InputStreamReader streamReader) {
    HeaderColumnNameTranslateMappingStrategy<Feature> strategy = new HeaderColumnNameTranslateMappingStrategy();
    Map<String, String> columnMap = new HashMap();
    columnMap.put("FEATURE_NAME", "name");
    columnMap.put("STATE", "state");
    strategy.setColumnMapping(columnMap);
    strategy.setType(Feature.class);
    CSVReader reader = new CSVReader(streamReader);
    CsvToBeanFilter filter = new EmptyLineFilter(strategy);
    return new CsvToBean().parse(strategy, reader, filter);
 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM