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