ZetCode

Opencsv

上次修改时间:2024 年 7 月 4 日

在本文中,我们将展示如何使用 Opencsv 库,该库用于在 Java 中读取和写入 CSV 文件。我们提供了几个代码示例,用于在 Java 中使用 CSV。

CSV(逗号分隔值)格式是电子表格和数据库中使用的非常流行的数据导入和导出格式。

CSV 文件中的每一行都是一个数据记录。每个记录由一个或多个字段组成,字段之间用逗号分隔。尽管 CSV 格式非常简单,但可能存在许多差异,例如不同的分隔符、换行符或引用字符。

Opencsv 库

Opencsv 是一个非常简单的 Java CSV 解析器库。 它的开发是因为缺乏商业友好的许可证。

<dependencies>
    <dependency>
        <groupId>com.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>5.9</version>
    </dependency>
</dependencies>

这是 Opencsv 的 Maven 依赖项。

读取数据

以下示例从 CSV 文件中读取数字。

src/main/resources/numbers.csv
3,5,6,2,1,7,8
4,5,7,3,2,8,9

我们在 numbers.csv 文件中有两条数据记录。

Main.java
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;

import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

void main() throws IOException, CsvValidationException {

    var fileName = "src/main/resources/numbers.csv";

    try (var fr = new FileReader(fileName, StandardCharsets.UTF_8);
            var reader = new CSVReader(fr)) {

        String[] nextLine;

        while ((nextLine = reader.readNext()) != null) {

            for (var e : nextLine) {
                System.out.format("%s ", e);
            }
        }
    }
}

该示例从 numbers.csv 文件中读取数字并将它们打印到控制台。

var fileName = "src/main/resources/numbers.csv";

该文件位于 src/main/resources 目录中。

try (var fr = new FileReader(fileName, StandardCharsets.UTF_8);
    var reader = new CSVReader(fr)) {

CSVReader 是用于读取 CSV 文件的类。

while ((nextLine = reader.readNext()) != null) {

    for (String e: nextLine) {
        System.out.format("%s ", e);
    }
}

我们迭代读取器并将值打印到终端。 readNext 方法从缓冲区读取下一行并将其转换为字符串数组。

使用不同的分隔符读取

尽管名称如此,CSV 文件可以使用逗号以外的分隔符分隔。 以下示例展示了如何读取由管道符 | 分隔的数字。

src/main/resources/numbers.csv
1|2|3|4|5
6|7|3|9|8
9|1|1|0|2

我们有三行数字,用 | 字符分隔。

Main.java
import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvException;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

void main() throws IOException, CsvException {

    var fileName = "src/main/resources/numbers.csv";
    Path myPath = Paths.get(fileName);

    CSVParser parser = new CSVParserBuilder().withSeparator('|').build();

    try (var br = Files.newBufferedReader(myPath,  StandardCharsets.UTF_8);
         var reader = new CSVReaderBuilder(br).withCSVParser(parser)
                 .build()) {

        List<String[]> rows = reader.readAll();

        for (String[] row : rows) {

            for (String e : row) {

                System.out.format("%s ", e);
            }

            System.out.println();
        }
    }
}

该示例从 numbers.csv 文件中读取值并将它们打印到控制台。

CSVParser parser = new CSVParserBuilder().withSeparator('|').build();

创建了一个带有特定解析器字符的 CSVParser

try (var br = Files.newBufferedReader(myPath,  StandardCharsets.UTF_8);
    var reader = new CSVReaderBuilder(br).withCSVParser(parser)
            .build()) {

使用 CSVReaderBuilder 创建了一个 CSVReader

List<String[]> rows = reader.readAll();

我们使用 readAll 方法一次性将所有元素读入一个列表。 此方法不应用于大型文件。

写入数据

CSVWriter 类用于将数据写入 CSV 文件。

Main.java
import com.opencsv.CSVWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

void main() throws IOException {

    String[] entries = { "book", "coin", "pencil", "cup" };
    String fileName = "src/main/resources/items.csv";

    try (var fos = new FileOutputStream(fileName); 
            var osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            var writer = new CSVWriter(osw)) {

        writer.writeNext(entries);
    }
}

该示例将数组中的数据写入 items.csv 文件。 该文件被写入项目根目录。 writeNext 方法将元素数组写入文件。

在下一个代码示例中,我们一次性写入所有数据。

Main.java
import com.opencsv.CSVWriter;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

void main() throws IOException {

    String[] items1 = {"book", "coin", "pencil"};
    String[] items2 = {"pen", "chair", "lamp"};
    String[] items3 = {"ball", "bowl", "spectacles"};

    List<String[]> entries = new ArrayList<>();
    entries.add(items1);
    entries.add(items2);
    entries.add(items3);

    String fileName = "src/main/resources/items.csv";

    try (var fos = new FileOutputStream(fileName);
            var osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            var writer = new CSVWriter(osw)) {

        writer.writeAll(entries);
    }
}

该示例使用 writeAll 方法将数组列表写入 items.csv 文件。

SQL 数据到 CSV 文件

以下示例从数据库表中检索数据并将其写入 CSV 文件。 我们使用 PostgreSQL 数据库。

除了 opencsv 工件之外,我们还需要 postgresql 驱动程序工件。

cars_postgres.sql
-- SQL for the cars table

CREATE TABLE cars(id serial PRIMARY KEY, name VARCHAR(255), price INT);

INSERT INTO cars(name, price) VALUES('Audi', 52642);
INSERT INTO cars(name, price) VALUES('Mercedes', 57127);
INSERT INTO cars(name, price) VALUES('Skoda', 9000);
INSERT INTO cars(name, price) VALUES('Volvo', 29000);
INSERT INTO cars(name, price) VALUES('Bentley', 350000);
INSERT INTO cars(name, price) VALUES('Citroen', 21000);
INSERT INTO cars(name, price) VALUES('Hummer', 41400);
INSERT INTO cars(name, price) VALUES('Volkswagen', 21600);

这是我们从中检索数据的 Cars 表。

Main.java
import com.opencsv.CSVWriter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;


void main() {

    String url = "jdbc:postgresql://:5432/testdb";
    String user = "postgres";
    String password = "s$cret";

    String fileName = "src/main/resources/cars.csv";
    Path myPath = Paths.get(fileName);

    try (var con = DriverManager.getConnection(url, user, password);
         var pst = con.prepareStatement("SELECT * FROM cars");
         var rs = pst.executeQuery()) {

        try (var writer = new CSVWriter(Files.newBufferedWriter(myPath,
                StandardCharsets.UTF_8), CSVWriter.DEFAULT_SEPARATOR,
                CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER,
                CSVWriter.DEFAULT_LINE_END)) {

            writer.writeAll(rs, true);
        }

    } catch (SQLException | IOException ex) {
        Logger.getLogger(getClass().getName()).log(
                Level.SEVERE, ex.getMessage(), ex);
    }
}

在该示例中,我们连接到 PostgreSQL 数据库并从 Cars 表中检索所有行。 数据被写入 cars.csv 文件。

try (var con = DriverManager.getConnection(url, user, password);
    var pst = con.prepareStatement("SELECT * FROM cars");
    var rs = pst.executeQuery()) {

我们使用驱动程序管理器连接到数据库表并执行 SELECT * FROM cars 语句。

try (var writer = new CSVWriter(Files.newBufferedWriter(myPath,
    StandardCharsets.UTF_8), CSVWriter.DEFAULT_SEPARATOR,
    CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.NO_ESCAPE_CHARACTER,
    CSVWriter.DEFAULT_LINE_END)) {

    writer.writeAll(rs, true);
}

我们创建一个 CSVWriter,其中包含默认分隔符、无引用字符、无转义字符和默认行尾。

writer.writeAll(rs, true);

writeAll 方法将 java.sql.ResultSet 作为参数。 第二个参数指定是否应包含字段标头。

Opencsv 映射到 JavaBeans

CsvToBean 用于将 CSV 数据映射到 JavaBeans。

按列名映射

使用 HeaderColumnNameMappingStrategy,我们可以使用 CSV 文件第一行中的列名将 CSV 数据映射到 Java 对象

src/main/resources/cars.csv
id,name,price
1,Audi,52642
2,Mercedes,57127
3,Skoda,9000
4,Volvo,29000
5,Bentley,350000
6,Citroen,21000
7,Hummer,41400
8,Volkswagen,21600
9,Toyota,26700

这是 cars.csv 文件。 第一条记录包含列名。

Main.java
import com.opencsv.bean.CsvBindByName;

public class Car {

    @CsvBindByName
    private int id;

    @CsvBindByName
    private String name;

    @CsvBindByName
    private int price;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {

        var builder = new StringBuilder();
        builder.append("Car{id=").append(id).append(", name=")
                .append(name).append(", price=").append(price).append("}");

        return builder.toString();
    }
}

Car 是一个 JavaBean。 它包含 @CsvBindByName 注释,用于将 bean 属性映射到 CSV 列。

Main.java
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

void main() throws IOException {

    String fileName = "src/main/resources/cars.csv";
    Path myPath = Paths.get(fileName);

    try (BufferedReader br = Files.newBufferedReader(myPath,
            StandardCharsets.UTF_8)) {

        HeaderColumnNameMappingStrategy<Car> strategy
                = new HeaderColumnNameMappingStrategy<>()>
        strategy.setType(Car.class);

        CsvToBean<Car> csvToBean = new CsvToBeanBuilder<Car>(br)
                .withMappingStrategy(strategy)
                .withIgnoreLeadingWhiteSpace(true)
                .build();

        List<Car> cars = csvToBean.parse();

        cars.forEach(System.out::println);
    }
}

该示例从 cars.csv 文件中读取数据并将它们映射到 Car 对象。 它使用 HeaderColumnNameMappingStrategy

HeaderColumnNameMappingStrategy<Car> strategy
        = new HeaderColumnNameMappingStrategy<>();
strategy.setType(Car.class);

HeaderColumnNameMappingStrategy 使用 CSV 文件第一行中的列名将数据映射到对象。 列顺序无关紧要。

CsvToBean<Car> csvToBean = new CsvToBeanBuilder<Car>(br)
    .withMappingStrategy(strategy)
    .withIgnoreLeadingWhiteSpace(true)
    .build();

使用 CsvToBeanBuilder 创建一个 CsvToBean。 我们指定映射策略。

List<Car> cars = csvToBean.parse();

使用 CsvToBeanparse 方法,我们将 CSV 数据解析为列表。

cars.forEach(System.out::println);

我们遍历 bean 列表并将它们打印到控制台。

按列位置映射

ColumnPositionMappingStrategy 按其位置映射列。

src/main/resources/cars.csv
1,Audi,52642
2,Mercedes,57127
3,Skoda,9000
4,Volvo,29000
5,Bentley,350000
6,Citroen,21000
7,Hummer,41400
8,Volkswagen,21600
9,Toyota,26700

这是 cars.csv 文件。

Car.java
import com.opencsv.bean.CsvBindByPosition;

public class Car {

    @CsvBindByPosition(position = 0)
    private int id;

    @CsvBindByPosition(position = 1)
    private String name;

    @CsvBindByPosition(position = 2)
    private int price;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {

        var builder = new StringBuilder();
        builder.append("Car{id=").append(id).append(", name=")
                .append(name).append(", price=").append(price).append("}");

        return builder.toString();
    }
}

@CsvBindByPosition 指定 CSV 输入的列号和 bean 中的字段之间的绑定。

Main.java
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

void main() throws IOException {

    String fileName = "src/main/resources/cars.csv";
    Path myPath = Paths.get(fileName);

    try (BufferedReader br = Files.newBufferedReader(myPath,
            StandardCharsets.UTF_8)) {

        ColumnPositionMappingStrategy<Car> strategy = new ColumnPositionMappingStrategy<>();
        strategy.setType(Car.class);
        String[] fields = {"id", "name", "price"};
        strategy.setColumnMapping(fields);

        CsvToBean<Car> csvToBean = new CsvToBeanBuilder<Car>(br)
                .withMappingStrategy(strategy)
                .withIgnoreLeadingWhiteSpace(true)
                .build();

        List<Car> cars = csvToBean.parse();

        cars.forEach(System.out::println);
    }
}

该示例从 cars.csv 文件中读取数据并将它们映射到 Car 对象。 它使用 ColumnPositionMappingStrategy

ColumnPositionMappingStrategy<Car> strategy = new ColumnPositionMappingStrategy<>();
strategy.setType(Car.class);
String[] fields = {"id", "name", "price"};
strategy.setColumnMapping(fields);

我们创建一个 ColumnPositionMappingStrategy。 使用 setColumnMapping 我们设置要映射的列名。

Opencsv 使用 StatefulBeanToCsv 写入 JavaBeans

在下一个示例中,我们使用 StatefulBeanToCsv 将 JavaBeans 写入 CSV。

Car.java
public class Car {

    private int id;
    private String name;
    private int price;

    public Car() {
    }

    public Car(int id, String name, int price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {

        var builder = new StringBuilder();
        builder.append("Car{id=").append(id).append(", name=")
                .append(name).append(", price=").append(price).append("}");

        return builder.toString();
    }
}

这是一个 Car bean。

Main.java
import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvDataTypeMismatchException;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

void main() {

    String fileName = "src/main/resources/cars.csv";
    Path myPath = Paths.get(fileName);

    var cars = List.of(new Car(1, "Audi", 52642),
            new Car(2, "Mercedes", 57127),
            new Car(3, "Skoda", 9000),
            new Car(4, "Volvo", 29000));


    try (var writer = Files.newBufferedWriter(myPath, StandardCharsets.UTF_8)) {

        StatefulBeanToCsv<Car> beanToCsv = new StatefulBeanToCsvBuilder<Car>(writer)
                .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
                .build();

        beanToCsv.write(cars);

    } catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException |
             IOException ex) {
        Logger.getLogger(getClass().getName()).log(
                Level.SEVERE, ex.getMessage(), ex);
    }
}

该示例创建一个汽车对象列表并将它们写入 CSV 文件。

var cars = List.of(new Car(1, "Audi", 52642),
        new Car(2, "Mercedes", 57127),
        new Car(3, "Skoda", 9000),
        new Car(4, "Volvo", 29000));

我们创建一个汽车对象列表。

StatefulBeanToCsv<Car> beanToCsv = new StatefulBeanToCsvBuilder<Car>(writer)
    .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
    .build();

使用 StatefulBeanToCsvBuilder 创建一个 StatefulBeanToCsv

beanToCsv.write(cars);

bean 被写入文件。

在本文中,我们使用了 Opencsv 库。 我们从 CSV 文件中读取了数据,将数据写入 CSV 文件,将数据从数据库表导出到 CSV 文件,并将 CSV 数据映射到 bean。

来源

Java Opencsv 文档

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程