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

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

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

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);

}  

不幸的是,这没有用,因为即使该行是空白的,它仍然是 returns 一个元素数组——实际上是空字符串。检查单个 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;
}

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

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
}

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

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

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 版(2020 年 2 月 2 日),根据功能请求 #120.

,CsvToBeanFilter 已被弃用

这是基于 @Martin's :

的 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 流 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 删除空行。由于解析器 returns 一个包含单个空字符串的数组用于空行,这就是它正在检查。”并列出了如何执行此操作的示例:

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);
 }

您可以使用带有 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;
    })