Автор оригинала: Pankaj Kumar.
Добро пожаловать в учебник по синтаксическому анализу Java CSV. CSV-файлы являются одним из наиболее широко используемых форматов для передачи данных из одной системы в другую. Поскольку CSV-файлы поддерживаются в Microsoft Excel, они также могут быть легко использованы нетехниками.
Синтаксический анализатор Java CSV
К сожалению, у нас нет встроенного синтаксического анализатора Java CSV.
Если CSV-файл действительно прост и не содержит никаких специальных символов, то мы можем использовать класс Java Scanner для анализа CSV-файлов, но в большинстве случаев это не так. Вместо того, чтобы писать сложную логику для синтаксического анализа, лучше использовать инструменты с открытым исходным кодом, которые у нас есть для анализа и записи CSV-файлов.
Существует три API с открытым исходным кодом для работы с CSV.
- OpenCSV
- CSV-файл Apache Commons
- Супер CSV
Мы рассмотрим все эти парсеры java CSV один за другим.
Предположим, у нас есть CSV-файл в виде:
Предположим, у нас есть CSV-файл в виде:
ID,Name,Role,Salary 1,Pankaj Kumar,CEO,"5,000USD" 2,Lisa,Manager,500USD 3,David,,1000USD
и мы хотим разобрать его на список объектов сотрудников.
package com.journaldev.parser.csv;
public class Employee {
private String id;
private String name;
private String role;
private String salary;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getSalary() {
return salary;
}
public void setSalary(String salary) {
this.salary = salary;
}
@Override
public String toString(){
return "ID="+id+",Name="+name+",Role="+role+",Salary="+salary+"\n";
}
}
1. OpenCSV
Мы увидим, как мы можем использовать java-анализатор OpenCSV для чтения CSV-файла в объект java, а затем записи CSV из объекта java. Загрузите библиотеки Opencv с веб-сайта SourceForge и включите их в путь к классам.
Если вы используете Maven, включите его с зависимостью ниже.
com.opencsv opencsv 3.8
Для анализа CSV-файла мы можем использовать CSVReader для анализа каждой строки в списке объектов. CSVParser также предоставляет возможность считывать все данные сразу, а затем анализировать их.
OpenCSV предоставляет CsvToBean класс, который мы можем использовать с HeaderColumnNameMappingStrategy объектом для автоматического сопоставления CSV со списком объектов.
Для записи данных CSV нам нужно создать список строкового массива, а затем использовать класс CSVWriter , чтобы записать его в файл или любой другой объект записи.
package com.journaldev.parser.csv;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;
import au.com.bytecode.opencsv.bean.CsvToBean;
import au.com.bytecode.opencsv.bean.HeaderColumnNameTranslateMappingStrategy;
public class OpenCSVParserExample {
public static void main(String[] args) throws IOException {
List emps = parseCSVFileLineByLine();
System.out.println("**********");
parseCSVFileAsList();
System.out.println("**********");
parseCSVToBeanList();
System.out.println("**********");
writeCSVData(emps);
}
private static void parseCSVToBeanList() throws IOException {
HeaderColumnNameTranslateMappingStrategy beanStrategy = new HeaderColumnNameTranslateMappingStrategy();
beanStrategy.setType(Employee.class);
Map columnMapping = new HashMap();
columnMapping.put("ID", "id");
columnMapping.put("Name", "name");
columnMapping.put("Role", "role");
//columnMapping.put("Salary", "salary");
beanStrategy.setColumnMapping(columnMapping);
CsvToBean csvToBean = new CsvToBean();
CSVReader reader = new CSVReader(new FileReader("employees.csv"));
List emps = csvToBean.parse(beanStrategy, reader);
System.out.println(emps);
}
private static void writeCSVData(List emps) throws IOException {
StringWriter writer = new StringWriter();
CSVWriter csvWriter = new CSVWriter(writer,'#');
List data = toStringArray(emps);
csvWriter.writeAll(data);
csvWriter.close();
System.out.println(writer);
}
private static List toStringArray(List emps) {
List records = new ArrayList();
//add header record
records.add(new String[]{"ID","Name","Role","Salary"});
Iterator it = emps.iterator();
while(it.hasNext()){
Employee emp = it.next();
records.add(new String[]{emp.getId(),emp.getName(),emp.getRole(),emp.getSalary()});
}
return records;
}
private static List parseCSVFileLineByLine() throws IOException {
//create CSVReader object
CSVReader reader = new CSVReader(new FileReader("employees.csv"), ',');
List emps = new ArrayList();
//read line by line
String[] record = null;
//skip header row
reader.readNext();
while((record = reader.readNext()) != null){
Employee emp = new Employee();
emp.setId(record[0]);
emp.setName(record[1]);
emp.setRole(record[2]);
emp.setSalary(record[3]);
emps.add(emp);
}
reader.close();
System.out.println(emps);
return emps;
}
private static void parseCSVFileAsList() throws IOException {
//create CSVReader object
CSVReader reader = new CSVReader(new FileReader("employees.csv"), ',');
List emps = new ArrayList();
//read all lines at once
List records = reader.readAll();
Iterator iterator = records.iterator();
//skip header row
iterator.next();
while(iterator.hasNext()){
String[] record = iterator.next();
Employee emp = new Employee();
emp.setId(record[0]);
emp.setName(record[1]);
emp.setRole(record[2]);
emp.setSalary(record[3]);
emps.add(emp);
}
reader.close();
System.out.println(emps);
}
}
Когда мы запускаем приведенный выше пример программы OpenCSV, мы получаем следующий результат.
[ID=1,Name=Pankaj Kumar,Role=CEO,Salary=5,000USD , ID=2,Name=Lisa,Role=Manager,Salary=500USD , ID=3,Name=David,Role=,Salary=1000USD ] ********** [ID=1,Name=Pankaj Kumar,Role=CEO,Salary=5,000USD , ID=2,Name=Lisa,Role=Manager,Salary=500USD , ID=3,Name=David,Role=,Salary=1000USD ] ********** [ID=1,Name=Pankaj Kumar,Role=CEO,Salary=null , ID=2,Name=Lisa,Role=Manager,Salary=null , ID=3,Name=David,Role=,Salary=null ] ********** "ID"#"Name"#"Role"#"Salary" "1"#"Pankaj Kumar"#"CEO"#"5,000USD" "2"#"Lisa"#"Manager"#"500USD" "3"#"David"#""#"1000USD"
Как вы можете видеть, мы также можем установить символы-разделители при разборе или записи данных CSV в java-анализаторе OpenCSV.
2. CSV-файл Apache Commons
Вы можете загрузить двоичные файлы CSV Apache Commons или включить зависимости с помощью maven, как показано ниже.
org.apache.commons commons-csv 1.3
CSV-анализатор Apache Commons прост в использовании, и CSVParser класс используется для анализа данных CSV, а CSVPrinter используется для записи данных.
Пример кода для анализа приведенного выше CSV-файла в список объектов сотрудников приведен ниже.
package com.journaldev.parser.csv;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
public class ApacheCommonsCSVParserExample {
public static void main(String[] args) throws FileNotFoundException, IOException {
//Create the CSVFormat object
CSVFormat format = CSVFormat.RFC4180.withHeader().withDelimiter(',');
//initialize the CSVParser object
CSVParser parser = new CSVParser(new FileReader("employees.csv"), format);
List emps = new ArrayList();
for(CSVRecord record : parser){
Employee emp = new Employee();
emp.setId(record.get("ID"));
emp.setName(record.get("Name"));
emp.setRole(record.get("Role"));
emp.setSalary(record.get("Salary"));
emps.add(emp);
}
//close the parser
parser.close();
System.out.println(emps);
//CSV Write Example using CSVPrinter
CSVPrinter printer = new CSVPrinter(System.out, format.withDelimiter('#'));
System.out.println("********");
printer.printRecord("ID","Name","Role","Salary");
for(Employee emp : emps){
List empData = new ArrayList();
empData.add(emp.getId());
empData.add(emp.getName());
empData.add(emp.getRole());
empData.add(emp.getSalary());
printer.printRecord(empData);
}
//close the printer
printer.close();
}
}
Когда мы запускаем вышеуказанную программу, мы получаем следующий результат.
[ID=1,Name=Pankaj Kumar,Role=CEO,Salary=5,000USD , ID=2,Name=Lisa,Role=Manager,Salary=500USD , ID=3,Name=David,Role=,Salary=1000USD ] ******** ID#Name#Role#Salary 1#Pankaj Kumar#CEO#5,000USD 2#Lisa#Manager#500USD 3#David##1000USD
3. Супер CSV
В поисках хороших анализаторов CSV я видел, как многие разработчики рекомендовали SuperCSV в StackOverflow. Поэтому я решил попробовать. Загрузите библиотеки Super CSV с веб-сайта SourceForge и включите файл jar в путь сборки проекта.
Если вы используете Maven, просто добавьте зависимость ниже.
net.sf.supercsv super-csv 2.4.0
Для разбора CSV-файла на список объектов нам необходимо создать экземпляр CsvBeanReader . Мы можем установить конкретные правила для ячеек, используя Процессор ячеек массив. Мы можем использовать его для чтения непосредственно из CSV-файла в java-компонент и наоборот.
Если нам нужно записать данные CSV, процесс аналогичен, и мы должны использовать CsvBeanWriter класс.
package com.journaldev.parser.csv;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.constraint.NotNull;
import org.supercsv.cellprocessor.constraint.UniqueHashCode;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;
public class SuperCSVParserExample {
public static void main(String[] args) throws IOException {
List emps = readCSVToBean();
System.out.println(emps);
System.out.println("******");
writeCSVData(emps);
}
private static void writeCSVData(List emps) throws IOException {
ICsvBeanWriter beanWriter = null;
StringWriter writer = new StringWriter();
try{
beanWriter = new CsvBeanWriter(writer, CsvPreference.STANDARD_PREFERENCE);
final String[] header = new String[]{"id","name","role","salary"};
final CellProcessor[] processors = getProcessors();
// write the header
beanWriter.writeHeader(header);
//write the bean's data
for(Employee emp: emps){
beanWriter.write(emp, header, processors);
}
}finally{
if( beanWriter != null ) {
beanWriter.close();
}
}
System.out.println("CSV Data\n"+writer.toString());
}
private static List readCSVToBean() throws IOException {
ICsvBeanReader beanReader = null;
List emps = new ArrayList();
try {
beanReader = new CsvBeanReader(new FileReader("employees.csv"),
CsvPreference.STANDARD_PREFERENCE);
// the name mapping provide the basis for bean setters
final String[] nameMapping = new String[]{"id","name","role","salary"};
//just read the header, so that it don't get mapped to Employee object
final String[] header = beanReader.getHeader(true);
final CellProcessor[] processors = getProcessors();
Employee emp;
while ((emp = beanReader.read(Employee.class, nameMapping,
processors)) != null) {
emps.add(emp);
}
} finally {
if (beanReader != null) {
beanReader.close();
}
}
return emps;
}
private static CellProcessor[] getProcessors() {
final CellProcessor[] processors = new CellProcessor[] {
new UniqueHashCode(), // ID (must be unique)
new NotNull(), // Name
new Optional(), // Role
new NotNull() // Salary
};
return processors;
}
}
Когда мы запускаем пример программы Super CSV выше, мы получаем результат ниже.
[ID=1,Name=Pankaj Kumar,Role=CEO,Salary=5,000USD , ID=2,Name=Lisa,Role=Manager,Salary=500USD , ID=3,Name=David,Role=null,Salary=1000USD ] ****** CSV Data id,name,role,salary 1,Pankaj Kumar,CEO,"5,000USD" 2,Lisa,Manager,500USD 3,David,,1000USD
Как вы можете видеть, поле Роли установлено как необязательное, потому что для третьей строки оно пустое. Теперь , если мы изменим это на Not Null , мы получим следующее исключение.
Exception in thread "main" org.supercsv.exception.SuperCsvConstraintViolationException: null value encountered
processor=org.supercsv.cellprocessor.constraint.NotNull
context={lineNo=4, rowNo=4, columnNo=3, rowSource=[3, David, null, 1000USD]}
at org.supercsv.cellprocessor.constraint.NotNull.execute(NotNull.java:71)
at org.supercsv.util.Util.executeCellProcessors(Util.java:93)
at org.supercsv.io.AbstractCsvReader.executeProcessors(AbstractCsvReader.java:203)
at org.supercsv.io.CsvBeanReader.read(CsvBeanReader.java:206)
at com.journaldev.parser.csv.SuperCSVParserExample.readCSVToBean(SuperCSVParserExample.java:66)
at com.journaldev.parser.csv.SuperCSVParserExample.main(SuperCSVParserExample.java:23)
Таким образом, SuperCSV предоставляет нам возможность использовать условную логику для полей, которые недоступны с другими анализаторами CSV. Он прост в использовании, и кривая обучения также очень мала.
Это все для примера учебника по синтаксическому анализу Java CSV. Следует ли использовать OpenCSV, Apache Commons CSV или SuperCSV, зависит от ваших требований, и все они кажутся простыми в использовании.