Рубрики
Без рубрики

Синтаксический анализатор Java CSV

Синтаксический анализатор Java CSV. Пример Opencv, CSV Apache Commons, Супер CSV. Java CSV Writer, CSVReader java, CSVWriter, CsvToBean, CSVParser, зависимости Maven.

Автор оригинала: Pankaj Kumar.

Добро пожаловать в учебник по синтаксическому анализу Java CSV. CSV-файлы являются одним из наиболее широко используемых форматов для передачи данных из одной системы в другую. Поскольку CSV-файлы поддерживаются в Microsoft Excel, они также могут быть легко использованы нетехниками.

Синтаксический анализатор Java CSV

К сожалению, у нас нет встроенного синтаксического анализатора Java CSV.

Если CSV-файл действительно прост и не содержит никаких специальных символов, то мы можем использовать класс Java Scanner для анализа CSV-файлов, но в большинстве случаев это не так. Вместо того, чтобы писать сложную логику для синтаксического анализа, лучше использовать инструменты с открытым исходным кодом, которые у нас есть для анализа и записи CSV-файлов.

Существует три API с открытым исходным кодом для работы с CSV.

  1. OpenCSV
  2. CSV-файл Apache Commons
  3. Супер 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, зависит от ваших требований, и все они кажутся простыми в использовании.