如何使用来自 apache.commons 的 CSVParser 以任何顺序读取 CSV 列
How to read in CSV columns in any order using CSVParser from apache.commons
我有一个包含以下格式数据的 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] 的顺序硬编码的,最后,城市]。 (用户 object 也必须按照 id、first、last、city 的精确顺序创建字段)
我知道我可以使用 'withHeader' 选项,但这也需要我像这样预先定义 header 列顺序:
String header = "id,first,last,city";
CSVParser csvParser = new CSVParser(reader, CSVFormat.EXCEL.withHeader(header.split(",")));
我也知道有一个 built in function getHeaderNames(),但它只会在我已经将它们作为字符串传入后才得到 header(再次进行硬编码)。因此,如果我传入 header 字符串“last,first,id,city”,它将 return 与列表中的字符串完全相同。
有没有一种方法可以将这些位组合起来以在 csv 中读取,无论列顺序是什么,并定义我的 'User' object 以及按顺序传递的字段(id、first、last ,城市)?
我们需要告诉解析器为我们处理 header 行。我们将其指定为 CSVFormat
的一部分,因此我们将创建如下自定义格式:
CSVFormat csvFormat = CSVFormat.RFC4180.withFirstRecordAsHeader();
使用问题代码 DEFAULT
, but this is based on RFC4180
代替。比较它们 side-by-side:
DEFAULT RFC4180 Comment
=================================== =========================== ========================
withDelimiter(',') withDelimiter(',') Same
withQuote('"') withQuote('"') Same
withRecordSeparator("\r\n") withRecordSeparator("\r\n") Same
withIgnoreEmptyLines(true) withIgnoreEmptyLines(false) Don't ignore blank lines
withAllowDuplicateHeaderNames(true) - Don't allow duplicates
=================================== =========================== ========================
withFirstRecordAsHeader() We need this
有了这个改变,我们可以调用 get(String name)
instead of get(int i)
:
User currentUser = new User(
Integer.parseInt(csvRecord.get("id")),
csvRecord.get("first"),
csvRecord.get("last"),
csvRecord.get("city")
);
注意 CSVParser
实现了 Iterable<CSVRecord>
,所以我们可以使用 for-each 循环,这使得代码看起来像这样:
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
我有一个包含以下格式数据的 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] 的顺序硬编码的,最后,城市]。 (用户 object 也必须按照 id、first、last、city 的精确顺序创建字段)
我知道我可以使用 'withHeader' 选项,但这也需要我像这样预先定义 header 列顺序:
String header = "id,first,last,city";
CSVParser csvParser = new CSVParser(reader, CSVFormat.EXCEL.withHeader(header.split(",")));
我也知道有一个 built in function getHeaderNames(),但它只会在我已经将它们作为字符串传入后才得到 header(再次进行硬编码)。因此,如果我传入 header 字符串“last,first,id,city”,它将 return 与列表中的字符串完全相同。
有没有一种方法可以将这些位组合起来以在 csv 中读取,无论列顺序是什么,并定义我的 'User' object 以及按顺序传递的字段(id、first、last ,城市)?
我们需要告诉解析器为我们处理 header 行。我们将其指定为 CSVFormat
的一部分,因此我们将创建如下自定义格式:
CSVFormat csvFormat = CSVFormat.RFC4180.withFirstRecordAsHeader();
使用问题代码 DEFAULT
, but this is based on RFC4180
代替。比较它们 side-by-side:
DEFAULT RFC4180 Comment
=================================== =========================== ========================
withDelimiter(',') withDelimiter(',') Same
withQuote('"') withQuote('"') Same
withRecordSeparator("\r\n") withRecordSeparator("\r\n") Same
withIgnoreEmptyLines(true) withIgnoreEmptyLines(false) Don't ignore blank lines
withAllowDuplicateHeaderNames(true) - Don't allow duplicates
=================================== =========================== ========================
withFirstRecordAsHeader() We need this
有了这个改变,我们可以调用 get(String name)
instead of get(int i)
:
User currentUser = new User(
Integer.parseInt(csvRecord.get("id")),
csvRecord.get("first"),
csvRecord.get("last"),
csvRecord.get("city")
);
注意 CSVParser
实现了 Iterable<CSVRecord>
,所以我们可以使用 for-each 循环,这使得代码看起来像这样:
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