Автор оригинала: 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 { Listemps = 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); Listemps = 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 { Listemps = 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, зависит от ваших требований, и все они кажутся простыми в использовании.