OpenCSV là một thư viện Java nhẹ dùng để phân tích file CSV. Hôm nay chúng ta sẽ xem một ví dụ về việc đọc và ghi file CSV trong Java bằng OpenCSV.
OpenCSV
OpenCSV cung cấp hầu hết các tính năng cơ bản để phân tích file CSV. OpenCSV trở nên phổ biến vì Java không có sẵn bộ parser CSV tích hợp. Một số lớp quan trọng trong OpenCSV CSV parser bao gồm:
CSVReader
: Đây là lớp quan trọng nhất trong OpenCSV. Lớp CSVReader được sử dụng để phân tích file CSV. Chúng ta có thể đọc dữ liệu CSV từng dòng một hoặc đọc toàn bộ dữ liệu cùng lúc.CSVWriter
: Lớp CSVWriter được dùng để ghi dữ liệu CSV vào một Writer implementation. Bạn cũng có thể tùy chỉnh delimiter và quotes.CsvToBean
: CsvToBean dùng khi bạn muốn chuyển dữ liệu CSV thành các đối tượng Java.BeanToCsv
: BeanToCsv dùng để xuất Java beans ra file CSV.
OpenCSV Maven Dependency
Bạn có thể thêm thư viện OpenCSV bằng dependency Maven sau:
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.8</version>
</dependency>
Trước khi bắt đầu xem ví dụ chương trình, chúng ta cần có dữ liệu CSV demo và Java bean tương ứng. Dưới đây là file CSV mẫu của chúng ta: emps.csv
1,Pankaj Kumar,20,India
2,David Dan,40,USA
3,Lisa Ray,28,Germany
Dưới đây là lớp Java bean của chúng ta để chứa dữ liệu CSV.
package com.journaldev.csv.model;
public class Employee {
private String id;
private String name;
private String age;
private String country;
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 getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return "{" + id + "::" + name + "::" + age + "::" + country + "}";
}
}
Hãy cùng xem một số ví dụ phổ biến về parsing và writing CSV.
CSVReader
Ví dụ đầu tiên với CSVReader là đọc các dòng trong file CSV từng dòng một, sau đó chuyển đổi thành danh sách các đối tượng Employee.
package com.journaldev.csv.opencsv.parser;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.journaldev.csv.model.Employee;
import com.opencsv.CSVReader;
/**
* OpenCSV CSVReader Example, Read line by line
*
* @author pankaj
*
*/
public class OpenCSVReaderLineByLineExample {
public static void main(String[] args) throws IOException {
CSVReader reader = new CSVReader(new FileReader("emps.csv"), ',');
List<Employee> emps = new ArrayList<Employee>();
// read line by line
String[] record = null;
while ((record = reader.readNext()) != null) {
Employee emp = new Employee();
emp.setId(record[0]);
emp.setName(record[1]);
emp.setAge(record[2]);
emp.setCountry(record[3]);
emps.add(emp);
}
System.out.println(emps);
reader.close();
}
}
Ví dụ CSVReader ở trên khá dễ hiểu. Một điểm quan trọng là cần đóng CSVReader sau khi sử dụng để tránh rò rỉ bộ nhớ. Ngoài ra, bạn cũng có thể chỉ định ký tự delimiter nếu đang dùng ký tự khác. Ví dụ tiếp theo với CSVReader là đọc toàn bộ dữ liệu cùng một lúc bằng phương thức readAll()
của CSVReader.
package com.journaldev.csv.opencsv.parser;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.journaldev.csv.model.Employee;
import com.opencsv.CSVReader;
/**
* OpenCSV CSVReader Example, Read all at once
*
* @author pankaj
*
*/
public class OpenCSVReaderReadAllExample {
public static void main(String[] args) throws IOException {
CSVReader reader = new CSVReader(new FileReader("emps.csv"), ',');
List<Employee> emps = new ArrayList<Employee>();
List<String[]> records = reader.readAll();
Iterator<String[]> iterator = records.iterator();
while (iterator.hasNext()) {
String[] record = iterator.next();
Employee emp = new Employee();
emp.setId(record[0]);
emp.setName(record[1]);
emp.setAge(record[2]);
emp.setCountry(record[3]);
emps.add(emp);
}
System.out.println(emps);
reader.close();
}
}
CsvToBean
Trong hầu hết các trường hợp, chúng ta muốn chuyển CSV thành đối tượng Java. Khi đó, chúng ta có thể sử dụng CsvToBean. Dưới đây là một ví dụ đơn giản, cho thấy cách chuyển file CSV nhân viên của chúng ta thành danh sách các đối tượng Employee.
package com.journaldev.csv.opencsv.parser;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import com.journaldev.csv.model.Employee;
import com.opencsv.CSVReader;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
public class OpenCSVParseToBeanExample {
public static void main(String[] args) throws IOException {
CSVReader reader = new CSVReader(new FileReader("emps.csv"), ',');
ColumnPositionMappingStrategy<Employee> beanStrategy = new ColumnPositionMappingStrategy<Employee>();
beanStrategy.setType(Employee.class);
beanStrategy.setColumnMapping(new String[] {"id","name","age","country"});
CsvToBean<Employee> csvToBean = new CsvToBean<Employee>();
List<Employee> emps = csvToBean.parse(beanStrategy, reader);
System.out.println(emps);
}
}
ColumnPositionMappingStrategy
được dùng để ánh xạ chỉ số cột trong CSV với các fields của đối tượng Employee. Đôi khi file CSV của chúng ta cũng có header, ví dụ chúng ta có file emps1.csv
như dưới đây.
ID,NAME,age, country
1,Pankaj Kumar,20,India
2,David Dan,40,USA
3,Lisa Ray,28,Germany
Trong trường hợp này, chúng ta có thể sử dụng HeaderColumnNameMappingStrategy
làm MappingStrategy. Dưới đây là phương thức minh họa cách sử dụng HeaderColumnNameMappingStrategy.
// returning list of Employee for CSVWriter example demo data
public static List<Employee> parseCSVWithHeader() throws IOException {
CSVReader reader = new CSVReader(new FileReader("emps1.csv"), ',');
HeaderColumnNameMappingStrategy<Employee> beanStrategy = new HeaderColumnNameMappingStrategy<Employee>();
beanStrategy.setType(Employee.class);
CsvToBean<Employee> csvToBean = new CsvToBean<Employee>();
List<Employee> emps = csvToBean.parse(beanStrategy, reader);
System.out.println(emps);
reader.close();
return emps;
}
CSVWriter
Hãy cùng xem ví dụ về CSVWriter để ghi các đối tượng Java vào một Writer dưới dạng CSV. Chúng ta sẽ tái sử dụng phương thức ****parseCSVWithHeader()
đã định nghĩa ở trên.
package com.journaldev.csv.opencsv.parser;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.journaldev.csv.model.Employee;
import com.opencsv.CSVWriter;
public class OpenCSVWriterExample {
public static void main(String[] args) throws IOException {
StringWriter writer = new StringWriter();
//using custom delimiter and quote character
CSVWriter csvWriter = new CSVWriter(writer, '#', '\\'');
List<Employee> emps = OpenCSVParseToBeanExample.parseCSVWithHeader();
List<String[]> data = toStringArray(emps);
csvWriter.writeAll(data);
csvWriter.close();
System.out.println(writer);
}
private static List<String[]> toStringArray(List<Employee> emps) {
List<String[]> records = new ArrayList<String[]>();
// adding header record
records.add(new String[] { "ID", "Name", "Age", "Country" });
Iterator<Employee> it = emps.iterator();
while (it.hasNext()) {
Employee emp = it.next();
records.add(new String[] { emp.getId(), emp.getName(), emp.getAge(), emp.getCountry() });
}
return records;
}
}
Chú ý việc sử dụng delimiter tùy chỉnh khi ghi dữ liệu CSV. Chúng ta cũng đã chỉ định ký tự quotes để áp dụng cho các trường trong các cột CSV. Ví dụ CSVWriter ở trên sẽ tạo ra kết quả như sau.
[{1::Pankaj Kumar::20::India}, {2::David Dan::40::USA}, {3::Lisa Ray::28::Germany}]
'ID'#'Name'#'Age'#'Country'
'1'#'Pankaj Kumar'#'20'#'India'
'2'#'David Dan'#'40'#'USA'
'3'#'Lisa Ray'#'28'#'Germany'
OpenCSV CSVWriter với ResultSet
Đôi khi chúng ta muốn xuất dữ liệu từ các bảng trong cơ sở dữ liệu ra file CSV để làm backup. Việc này có thể thực hiện dễ dàng bằng phương thức CSVWriter writeAll(ResultSet rs, boolean includeColumnNames)
.
OpenCSV Annotation
OpenCSV cũng hỗ trợ annotation-based mapping. Một số annotation trong OpenCSV bao gồm:
CsvBindByName
: dùng để ánh xạ giữa tên cột trong CSV và trường trong bean.CsvBindByPosition
: dùng để ánh xạ giữa số thứ tự cột trong CSV và trường trong bean.CsvDate
: dùng để chuyển đổi dữ liệu thời gian.
Tuy nhiên, tôi không muốn sử dụng annotation của OpenCSV vì điều đó sẽ làm code của tôi phụ thuộc chặt vào OpenCSV. Đó là tất cả cho hướng dẫn ví dụ về OpenCSV.