我有一个csv文件,其中包含以下格式的一些数据:
id,first,last,city
1,john,doe,austin
2,jane,mary,seattle
到目前为止,我正在使用以下代码在csv中阅读:
String path = "./data/data.csv";
Map<Integer, User> map = new HashMap<>();
Reader reader = Files.newBufferedReader(Paths.get(path));
try (CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT)) {
List<CSVRecord> csvRecords = csvParser.getRecords();
for(int i=0; i < csvRecords.size(); i++){
if(0<i){//skip over header
CSVRecord csvRecord = csvRecords.get(i);
User currentUser = new User(
Double.valueOf(csvRecord.get(0)).intValue(),
Double.valueOf(csvRecord.get(1)),
Double.valueOf(csvRecord.get(2)),
Double.valueOf(csvRecord.get(3))
);
map.put(currentUser.getId(), currentUser);
}
}
} catch (IOException e){
System.out.println(e);
}
它获取正确的值,但如果这些值的顺序不同,比如[city,last,id,first],它将被错误地读取,因为读取是按照[id,first,last,city]的顺序硬编码的。(用户对象也必须按照id、first、last、city的确切顺序创建字段(
我知道我可以使用"withHeader"选项,但这也需要我提前定义标题列的顺序,如下所示:
String header = "id,first,last,city";
CSVParser csvParser = new CSVParser(reader, CSVFormat.EXCEL.withHeader(header.split(",")));
我也知道有一个内置的函数getHeaderNames((,但它只有在我已经将它们作为字符串传入后才能获得标题(再次进行编码(。因此,如果我传入标题字符串";last,first,id,city"它会在列表中返回该值。
有没有一种方法可以将这些位组合在csv中读取,无论列的顺序是什么,并用按顺序传递的字段(id、first、last、city(定义我的"User"对象?
我们需要告诉解析器为我们处理头行。我们将其指定为CSVFormat
的一部分,因此我们将创建这样的自定义格式:
CSVFormat csvFormat = CSVFormat.RFC4180.withFirstRecordAsHeader();
问题代码使用了DEFAULT
,但这是基于RFC4180
。并排比较:
DEFAULT RFC4180 Comment
=================================== =========================== ========================
withDelimiter(',') withDelimiter(',') Same
withQuote('"') withQuote('"') Same
withRecordSeparator("rn") withRecordSeparator("rn") Same
withIgnoreEmptyLines(true) withIgnoreEmptyLines(false) Don't ignore blank lines
withAllowDuplicateHeaderNames(true) - Don't allow duplicates
=================================== =========================== ========================
withFirstRecordAsHeader() We need this
有了这个变化,我们可以调用get(String name)
而不是get(int i)
:
User currentUser = new User(
Integer.parseInt(csvRecord.get("id")),
csvRecord.get("first"),
csvRecord.get("last"),
csvRecord.get("city")
);
注意,CSVParser
实现了Iterable<CSVRecord>
,所以我们可以为每个循环使用一个,这使得代码看起来像这样:
String path = "./data/data.csv";
Map<Integer, User> map = new HashMap<>();
try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(Paths.get(path)),
CSVFormat.RFC4180.withFirstRecordAsHeader())) {
for (CSVRecord csvRecord : csvParser) {
User currentUser = new User(
Integer.parseInt(csvRecord.get("id")),
csvRecord.get("first"),
csvRecord.get("last"),
csvRecord.get("city")
);
map.put(currentUser.getId(), currentUser);
}
}
即使列顺序发生变化,该代码也能正确解析文件,例如:
last,first,id,city
doe,john,1,austin
mary,jane,2,seattle