编辑: 更新超级CSV
2.0.0-β-1
请注意,API已在Super CSV
2.0.0-beta-1中更改(代码示例基于1.52)。现在
getCSVHeader(),所有读者都
getHeader()可以使用该方法(与
writeHeader作者一致)。
此外,
SuperCSVException已重命名为
SuperCsvException。
编辑: Super CSV 2.1.0更新
从2.1.0版开始,可以使用新方法 在 读取一行CSV 之后
执行单元处理器
executeProcessors()。有关更多信息,请参见项目网站上的此示例。请注意,这仅与有关
CsvListReader,因为它是唯一允许可变列长的读取器。
您是正确的-
CsvBeanReader不支持列数可变的CSV文件。根据大多数CSV规范(包括RFC
4180),每行的列数必须相同。
因此,(作为超级CSV开发人员)我不愿意将此功能添加到超级CSV中。如果您想出一种优雅的添加方式,请随时在项目的SourceForge网站上提出建议。这可能意味着要扩展一个新的阅读器
CsvBeanReader:它将不得不将读取和映射/处理分为两个单独的方法(除非您知道有多少列,否则您无法对bean的字段进行任何处理或映射)
。
简单的解决方案
解决此问题的简单方法(如果您可以控制要使用的CSV文件)是在编写CSV文件时简单地添加一个空白列(示例中的第一行的末尾带有逗号-
表示最后一列为空)。这样,您的CSV文件将是有效的(每行具有相同的列数),并且您可以
CsvBeanReader按照自己的方式使用。
如果那不可能,那么 一切就不会丢失 !
花式的解决方案
您可能已经意识到,
CsvBeanReader使用名称映射将CSV文件中的每一列与bean中的一个字段相关联,并使用CellProcessor数组来处理每一列。换句话说,如果要使用它,您必须知道有多少列(及其代表什么)。
CsvListReader另一方面,它非常原始,可以读取不同长度的行(因为它不需要处理或映射它们)。
因此,您可以通过同时使用两个阅读器读取文件来组合
CsvBeanReaderwith的所有功能
CsvListReader(如以下示例中所述):
CsvListReader用于找出有多少列,并
CsvBeanReader进行处理/映射。
请注意,这是假设仅可能不存在birthDate列(即,如果您无法确定缺少哪一列,它将不起作用)。
package example;import java.io.StringReader;import java.util.Date;import org.supercsv.cellprocessor.ParseDate;import org.supercsv.cellprocessor.ift.CellProcessor;import org.supercsv.exception.SuperCSVException;import org.supercsv.io.CsvBeanReader;import org.supercsv.io.CsvListReader;import org.supercsv.io.ICsvBeanReader;import org.supercsv.io.ICsvListReader;import org.supercsv.prefs.CsvPreference;public class VariableColumns { private static final String INPUT = "name,birthDate,cityn" + "John,New Yorkn" + "Sally,22/03/1974,Londonn" + "Jim,Sydney"; // cell processors private static final CellProcessor[] NORMAL_PROCESSORS = new CellProcessor[] {null, new ParseDate("dd/MM/yyyy"), null }; private static final CellProcessor[] NO_BIRTHDATE_PROCESSORS = new CellProcessor[] {null, null }; // name mappings private static final String[] NORMAL_HEADER = new String[] { "name", "birthDate", "city" }; private static final String[] NO_BIRTHDATE_HEADER = new String[] { "name", "city" }; public static void main(String[] args) { // using bean reader and list reader together (to read the same file) final ICsvBeanReader beanReader = new CsvBeanReader(new StringReader( INPUT), CsvPreference.STANDARD_PREFERENCE); final ICsvListReader listReader = new CsvListReader(new StringReader( INPUT), CsvPreference.STANDARD_PREFERENCE); try { // skip over header beanReader.getCSVHeader(true); listReader.getCSVHeader(true); while (listReader.read() != null) { final String[] nameMapping; final CellProcessor[] processors; if (listReader.length() == NORMAL_HEADER.length) { // all columns present - use normal header/processors nameMapping = NORMAL_HEADER; processors = NORMAL_PROCESSORS; } else if (listReader.length() == NO_BIRTHDATE_HEADER.length) { // one less column - birth date must be missing nameMapping = NO_BIRTHDATE_HEADER; processors = NO_BIRTHDATE_PROCESSORS; } else { throw new SuperCSVException( "unexpected number of columns: " + listReader.length()); } // can now use CsvBeanReader safely // (we know how many columns there are) Person person = beanReader.read(Person.class, nameMapping, processors); System.out.println(String.format( "Person: name=%s, birthDate=%s, city=%s", person.getName(), person.getBirthDate(), person.getCity())); } } catch (Exception e) { // handle exceptions here e.printStackTrace(); } finally { // close readers here } } public static class Person { private String name; private Date birthDate; private String city; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }}我希望这有帮助。
哦,您的
Entry类中的字段为何不遵循正常的命名约定(camelCase)有什么理由吗?如果将
header阵列更新为使用camelcase,则字段也可以是camelcase。



