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

jaxagkaj  于 2023-02-27  发布在  Java
关注(0)|答案(8)|浏览(405)

大家好!我的目标是让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);

}

不幸的是,这并不起作用,因为即使行是空的,它仍然返回一个元素数组-实际上是空字符串。检查单个字符串不是一个选项,因为有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;
    }
2hh7jdfx

2hh7jdfx1#

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

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

b4wnujal2#

对于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未被删除。

unguejic

unguejic3#

以下是基于@Martin的solution的lambdas更新解决方案:

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

bd1hkmkf4#

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

gr8qqesn5#

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

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
}
wecizke3

wecizke36#

如果不解析为Bean,可以使用Java Streams API来帮助过滤无效的CSV行。我的方法如下(其中is是包含CSV数据的java.io.InputStream示例,YourBean map(String[] row)是将CSV行Map到Java对象的Map方法:

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

vu8f3i0k7#

JavaDoc for CsvToBeanFilter声明“以下示例显示了如何使用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);
 }
u1ehiz5o

u1ehiz5o8#

另一种方法是重写类CsvToBeanFilterallowLine()方法,并在构建bean时将其传递给.withFilter()(Kotlin中的示例也适用于Java):

import com.opencsv.bean.CsvToBeanFilter
import com.opencsv.bean.CsvToBeanBuilder
import java.io.FileReader

class NonBlankLinesFilter : CsvToBeanFilter {
    override fun allowLine(line: Array<String?>?): Boolean {
        if (line == null) {
            return false
        }
        for (value in line) {
            if (!value.isNullOrBlank()) {
                return true
        }
    }
    return false
  }
}

fun main() {
  val reader = FileReader("path/to/csv/file.csv")

  val csvToBean = CsvToBeanBuilder<MyBean>(reader)
        .withType(MyBean::class.java)
        .withSeparator(',')
        .withFilter(NonBlankLinesFilter())
        .build()

  val uploadList = csvToBean.parse()

  println(uploadList)
}

相关问题