在我的应用程序中,我使用了很多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
我怎样才能轻松完成?
答案 0 :(得分:4)
有许多用Java编写的好框架来解析CSV文件并形成一个对象列表。 OpenCSV,JSefa& 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 website(direct 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)
我最近使用 Immutables 和 Jackson 解决了这个问题,如果您愿意使用这些库,我认为这是一个很好的方法。
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序列化非常健壮