如何轻松处理CSV文件到List <myclass> </myclass>

时间:2014-03-18 16:15:59

标签: java parsing csv filereader

在我的应用程序中,我使用了很多CSV文件,我必须阅读这些文件并根据它们构建列表。我想发现一个简单的方法来做到这一点。你知道任何简单的框架吗,而不使用配置文件的数量等?

例如,我有一个人类:

public class Person {
    String name;
    String surname;

    double shoeSize;
    boolean sex; // true: male, false:female

    public Person() {
    }

    public String getName() {
            return name;
    }

    public void setName(String name) {
            this.name = name;
    }

    public String getSurname() {
            return surname;
    }

    public void setSurname(String surname) {
            this.surname = surname;
    }

    public double getShoeSize() {
            return shoeSize;
    }

    public void setShoeSize(double shoeSize) {
            this.shoeSize = shoeSize;
    }

    public boolean isSe) {
            return sex;
    }

    public void setSeboolean sex) {
            this.sex = sex;
    }

}

对于本课程,我准备了CSV文件:

name,surname,shoesize,sex
Tom,Tommy,32,true
Anna,Anny,27,false

我怎样才能轻松完成?

7 个答案:

答案 0 :(得分:4)

有许多用Java编写的好框架来解析CSV文件并形成一个对象列表。 OpenCSVJSefa&amp; jCSV仅举几例。

根据您的要求,我相信jCSV最适合您。以下是jCSV的示例代码,您可以轻松使用它。

Reader reader = new FileReader("persons.csv");

CSVReader<Person> csvPersonReader = ...;

// read all entries at once
List<Person> persons = csvPersonReader.readAll();

// read each entry individually
Iterator<Person> it = csvPersonReader.iterator();
while (it.hasNext()) {
  Person p = it.next();
  // ...
}

此外,解析CSV文件并将其转换为列表并不是一件大事,而且可以实现而不使用任何框架,如下所示。

br = new BufferedReader(new FileReader(csvFileToRead));  
List<Person> personList = new ArrayList<>();
while ((line = br.readLine()) != null) {  
       // split on comma(',')  
       String[] personCsv = line.split(splitBy);  

       // create car object to store values  
       Person personObj = new Person();  

       // add values from csv to car object  
       personObj.setName(personCsv[0]);  
       personObj.setSurname(personCsv[1]);  
       personObj.setShoeSize(personCsv[2]);  
       personObj.setGender(personCsv[3]); 

       // adding car objects to a list  
       personList.add(personObj);         
} 

如果CSV列到bean对象的映射在实际情况下是复杂的,重复的或大的,那么可以使用DozerBeanMapper轻松完成。

希望这会对你有所帮助。

Shishir

答案 1 :(得分:2)

不确定是否需要使用外部库(并采用通常隐含的性能命中)。实现起来非常简单。如果不出意外,了解这样一个库中幕后发生的事情总是有帮助的:

public List<Person> readFile(String fileName) throws IOException {
    List<Person> result = new ArrayList<Person>();
    BufferedReader br = new BufferedReader(new FileReader(new File(fileName)));
    try {
        // Read first line
        String line = br.readLine();
        // Make sure file has correct headers
        if (line==null) throw new IllegalArgumentException("File is empty");
        if (!line.equals("name,surname,shoesize,sex"))
            throw new IllegalArgumentException("File has wrong columns: "+line);
        // Run through following lines
        while ((line = br.readLine()) != null) {
            // Break line into entries using comma
            String[] items = line.split(",");
            try {
                // If there are too many entries, throw a dummy exception, if
                // there are too few, the same exception will be thrown later
                if (items.length>4) throw new ArrayIndexOutOfBoundsException(); 
                // Convert data to person record
                Person person = new Person();
                person.setName    (                     items[0] );
                person.setSurname (                     items[1] );
                person.setShoeSize(Double .parseDouble (items[2]));
                person.setSex     (Boolean.parseBoolean(items[3]));
                result.add(person);
            } catch (ArrayIndexOutOfBoundsException|NumberFormatException|NullPointerException e) {
                // Caught errors indicate a problem with data format -> Print warning and continue
                System.out.println("Invalid line: "+ line);
            }
        }
        return result;
    } finally {
        br.close();
    }
}

请注意, catch 语句使用Java 7 multi-catch。对于较旧的Java版本,要么将其拆分为3个catch块,要么将ArrayIndexOutOfBoundsException|NumberFormatException|NullPointerException替换为Exception。后者通常不鼓励,因为它掩盖并忽略所有其他例外,但在一个简单的例子中,风险可能不会太高。

不幸的是,这个答案是针对你的问题的,但鉴于它非常直接,它也应该很容易适应其他情况......

你能做的另一件好事是将while循环中的line与正则表达式匹配,而不是简单地根据逗号分割它。这样你就可以一次性实现数据验证(例如,只匹配鞋号的合理数字)。

请注意,如果您的名称包含逗号,然后用引号括起来(例如“Jackson,Jr。”作为姓氏),则上述实现不起作用。如果您使用如上所述的正则表达式,或者通过检查姓氏的第一个字母并且如果它是引号,将项目[1]与项目[2]组合并使用项目[3],则可以“轻松”覆盖此案例]和项目[4]而不是鞋子和性别。这个特殊情况可能会被这里建议的大多数外部库所涵盖,所以如果您不担心任何依赖性,许可问题和性能命中,那么这些可能是更简单的方法......

答案 2 :(得分:2)

读取和序列化数据的最简单方法之一是使用Jackson库。 它还有CSV的扩展名,你可以找到wiki here

让我们说你有这样的Pojo:

@JsonPropertyOrder({ "name", "surname", "shoesize", "gender" })
public class Person {

    public String name;
    public String surname;
    public int shoesize;
    public String gender;

}

这样的CSV:

Tom,Tommy,32,m
Anna,Anny,27,f

然后阅读就像这样:

MappingIterator<Person> personIter = new CsvMapper().readerWithTypedSchemaFor(Person.class).readValues(csvFile);
List<Person> people = personIter.readAll();

这对我来说很简单,基本上您需要做的就是使用@JsonPropertyOrder注释在CSV文件中添加列顺序,然后使用上面两行读取文件。

答案 3 :(得分:1)

使用OpenCSV

这是一个完整的示例,它读取条目并将它们添加到List:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import au.com.bytecode.opencsv.CSVReader;

public class CSVReaderImplementor {
  private String fileName;
  private CSVReader reader;
  private List<String[]> entries;

  public CSVReaderImplementor(String fileName) throws IOException, FileNotFoundException {
    this.fileName = fileName;
    reader = new CSVReader(new FileReader(this.fileName));

    entries = reader.readAll();

  }

  public List getEntries() {
    return entries;
  }

  public static void main(String[] args) throws FileNotFoundException, IOException {
    CSVReaderImplementor cri = new CSVReaderImplementor("yourfile.csv");

    for(int i = 0; i < 50; i++) {
      System.out.println(cri.getEntries().get(i).toString());
    }
  }
}

返回List类型String[]。您可以遍历列表中每个条目的String数组,并使用每个索引处的值来填充Bean构造函数。

答案 4 :(得分:0)

opencsv是一个很好的简单解决方案。这是一个小而强大的图书馆。您可以从opencsv websitedirect download from sourceforge下载,使用deploy目录中的jar)或使用maven

java bean映射功能使它非常简单,因为您的CSV列名称与您的类的属性名称匹配(它忽略了不同的大小写)。

如何使用它:

Reader reader = // ... reader for the input file

// let it map the csv column headers to properties
CsvToBean<Person> csvPersons = new CsvToBean<Person>();
HeaderColumnNameMappingStrategy<Person> strategy = new HeaderColumnNameMappingStrategy<Person>();
strategy.setType(Person.class);

// parse the file and get a list of persons
List<Person> persons = csvPersons.parse(strategy, reader);

就是这样。

答案 5 :(得分:0)

我最近使用 ImmutablesJackson 解决了这个问题,如果您愿意使用这些库,我认为这是一个很好的方法。

Immutables 和 Jackson 整合得很好。以 OP 为例,您所要做的就是像这样指定 Immutables 类(注释符合片段显式性):

@org.immutables.value.Value.Immutable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(as = ImmutablePerson.class)
public interface Person {
    String getName();
    String getSurname();
    double getShoeSize();
    boolean getSex();
}

然后,使用 Jackson CSV module,您可以轻松地将 CSV 的每一行反序列化为 Immutables 为您生成的类:

List<Person> loadPeople(File personsCsvFile) throws IOException {
    CsvSchema schema = CsvSchema.emptySchema().withHeader();
    MappingIterator<Person> personsIterator = new CsvMapper()
            .readerFor(Person.class)
            .with(schema)
            .readValues(personsCsvFile);
    return personsIterator.readAll();
}

答案 6 :(得分:-1)

我认为SuperCSV + Dozer易于使用且对java bean CSV序列化非常健壮

http://supercsv.sourceforge.net/dozer.html