1. 简介

本文将带你快速上手 OpenCSV 4,一个用于处理 CSV 文件的强大 Java 库。它支持 CSV 文件的读写、序列化、反序列化及解析等操作。我们将通过多个示例,演示如何配置和使用 OpenCSV 4 完成实际开发任务。

2. 环境配置

首先通过 Maven 依赖引入 OpenCSV:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.8</version>
</dependency>

JAR 包可从 官网Maven 中央仓库 获取。

本文使用的示例 CSV 文件结构简单,包含两列四行:

colA,colB
A,B
C,D
G,G
G,F

3. 使用 Bean 还是原生 API

引入依赖后,可通过两种方式处理 CSV:

  1. 原生 API:使用 CSVReader/CSVWriter(适合简单操作)
  2. Bean 映射:使用 CsvToBean 将 CSV 转换为注解化的 POJO 对象

本文聚焦同步(阻塞)操作,异步处理将在后续文章讨论。⚠️ 同步方法会阻塞后续代码执行,生产环境建议改用异步方案。

3.1. CSVReader 原生读取

CSVReader 提供 readAll()readNext() 两种读取方式。先看 readAll() 同步读取全量数据:

public List<String[]> readAllLines(Path filePath) throws Exception {
    try (Reader reader = Files.newBufferedReader(filePath)) {
        try (CSVReader csvReader = new CSVReader(reader)) {
            return csvReader.readAll();
        }
    }
}

调用方式:

public List<String[]> readAllLinesExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()
    );
    return CsvReaderExamples.readAllLines(path);
}

再看 readNext() 逐行读取:

public List<String[]> readLineByLine(Path filePath) throws Exception {
    List<String[]> list = new ArrayList<>();
    try (Reader reader = Files.newBufferedReader(filePath)) {
        try (CSVReader csvReader = new CSVReader(reader)) {
            String[] line;
            while ((line = csvReader.readNext()) != null) {
                list.add(line);
            }
        }
    }
    return list;
}

调用方式:

public List<String[]> readLineByLineExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()
    );
    return CsvReaderExamples.readLineByLine(path);
}

进阶用法:使用 CSVReaderBuilder 灵活配置

CSVParser parser = new CSVParserBuilder()
    .withSeparator(',')
    .withIgnoreQuotations(true)
    .build();

CSVReader csvReader = new CSVReaderBuilder(reader)
    .withSkipLines(0)
    .withCSVParser(parser)
    .build();

CSVReaderBuilder 支持跳过表头、自定义分隔符、处理引号等配置。更多参数见官方文档

踩坑提醒:务必关闭 Reader 防止内存泄漏!

3.2. CSVWriter 原生写入

CSVWriter 支持逐行写入或全量写入。先看逐行写入:

public String writeLineByLine(List<String[]> lines, Path path) throws Exception {
    try (CSVWriter writer = new CSVWriter(new FileWriter(path.toString()))) {
        for (String[] line : lines) {
            writer.writeNext(line);
        }
    }
    return Helpers.readFile(path);
}

调用方式:

public String writeLineByLineExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenOneByOne.csv").toURI()
    ); 
    return CsvWriterExamples.writeLineByLine(Helpers.fourColumnCsvString(), path); 
}

再看全量写入:

public String writeAllLines(List<String[]> lines, Path path) throws Exception {
    try (CSVWriter writer = new CSVWriter(new FileWriter(path.toString()))) {
        writer.writeAll(lines);
    }
    return Helpers.readFile(path);
}

调用方式:

public String writeAllLinesExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenAll.csv").toURI()
    ); 
    return CsvWriterExamples.writeAllLines(Helpers.fourColumnCsvString(), path);
}

3.3. Bean 映射读取

OpenCSV 可将 CSV 映射为注解化的 POJO Bean。推荐使用 CsvToBeanBuilder(OpenCSV 4+ 标准用法)。

示例 Bean 定义:

public class SimplePositionBean  {
    @CsvBindByPosition(position = 0)
    private String exampleColOne;

    @CsvBindByPosition(position = 1)
    private String exampleColTwo;

    // getters and setters
}

✅ 通过 @CsvBindByPosition 按列位置映射,或用 @CsvBindByName 按列名映射。

创建通用基类提升复用性:

public class CsvBean { }

子类示例(按列名映射):

public class NamedColumnBean extends CsvBean {

    @CsvBindByName(column = "name")
    private String name;

    // 自动推断列名为 'Age'
    @CsvBindByName
    private int age;

    // getters and setters
}

同步读取实现:

public List<CsvBean> beanBuilderExample(Path path, Class clazz) throws Exception {
    CsvTransfer csvTransfer = new CsvTransfer();

    try (Reader reader = Files.newBufferedReader(path)) {
        CsvToBean<CsvBean> cb = new CsvToBeanBuilder<CsvBean>(reader)
         .withType(clazz)
         .build();

        csvTransfer.setCsvList(cb.parse());
    }
    return csvTransfer.getCsvList();
}

调用方式(位置映射):

public List<CsvBean> simplePositionBeanExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()
    ); 
    return BeanExamples.beanBuilderExample(path, SimplePositionBean.class); 
}

调用方式(列名映射):

public List<CsvBean> namedColumnBeanExample() throws Exception {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/namedColumn.csv").toURI()
    ); 
    return BeanExamples.beanBuilderExample(path, NamedColumnBean.class);
}

3.4. Bean 映射写入

使用 StatefulBeanToCsv 将 Bean 写入 CSV:

public String writeCsvFromBean(Path path) throws Exception {

    List<CsvBean> sampleData = Arrays.asList(
        new WriteExampleBean("Test1", "sfdsf", "fdfd"),
        new WriteExampleBean("Test2", "ipso", "facto")
    );
    
    try (Writer writer  = new FileWriter(path.toString())) {

        StatefulBeanToCsv<CsvBean> sbc = new StatefulBeanToCsvBuilder<CsvBean>(writer)
          .withQuotechar('\'')
          .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
          .build();

        sbc.write(sampleData);
    }
    return Helpers.readFile(path);
}

调用方式:

public String writeCsvFromBeanExample() {
    Path path = Paths.get(
      ClassLoader.getSystemResource("csv/writtenBean.csv").toURI()
    ); 
    return BeanExamples.writeCsvFromBean(path); 
}

4. 总结

本文介绍了 OpenCSV 的同步操作方案,涵盖:

  • 原生 CSVReader/CSVWriter API
  • Bean 映射的读写操作

完整代码示例见 GitHub 仓库。更多细节请参考官方文档


原始标题:Introduction to OpenCSV

» 下一篇: Jdbi 使用指南