参考文章:https://www.cnblogs.com/xiaogh/articles/17705590.html
OpenCSV 是一个用于 Java 的流行的库,用于读取和写入 CSV(逗号分隔值)文件。CSV 文件是一种常见的结构化数据存储和交换格式。
OpenCSV 提供了一种简单和方便的方式来在 Java 应用程序中处理 CSV 文件。
以下是 OpenCSV 的一些关键特点和用法示例:
读取 CSV 文件
写入 CSV 文件
自定义 CSV 解析和写入
OpenCSV 允许您自定义 CSV 解析和写入的各个方面,例如指定分隔符(默认是逗号)、处理引号等。
使用注解处理 CSV:
OpenCSV 支持使用注解将 Java 对象映射到 CSV 记录。可以使用注解来定义如何将字段映射到 CSV 列。
**@CsvBindByName:**通过列名来绑定字段。
你可以使用 column
属性指定 CSV 文件中的列名,OpenCSV 将根据列名将数据映射到相应的字段。
@CsvBindByName(column = "Name")
private String name;
@CsvBindByPosition:通过列的位置(索引)来绑定字段。
你可以使用 position
属性指定 CSV 文件中的列索引,OpenCSV 将根据索引将数据映射到相应的字段。
@CsvBindByPosition(position = 0)
private int id;
@CsvBindByType:用于指定字段的数据类型。
你可以使用 type
属性指定数据类型,OpenCSV 将尝试将 CSV 数据转换为指定的数据类型。
@CsvBindByType(type = BigDecimal.class)
private BigDecimal price;
@CsvDate:用于处理日期字段。
你可以使用 value
属性指定日期的格式,OpenCSV 将尝试将 CSV 数据解析为指定格式的日期。
@CsvDate("yyyy-MM-dd")
private Date birthDate;
@CsvCustomBindByName:自定义绑定器。
你可以使用 converter
属性指定一个自定义的绑定器类,该类负责将 CSV 数据转换为字段的值。
@CsvCustomBindByName(column = "Price", converter = CustomPriceConverter.class)
private BigDecimal price;
错误处理:
OpenCSV 提供了处理 CSV 解析和写入过程中错误的机制,例如:处理格式不正确的 CSV 记录。
处理文件头:
可以通过单独读取文件头并使用文件头来映射数据到 Java 对象,轻松处理带有标题的 CSV 文件。
CSV Bean 映射:
OpenCSV 允许将 CSV 记录直接映射到 Java Bean,使用注解来定义映射关系,这样可以轻松处理结构化数据。
将 CSV 转换为 SQL:
OpenCSV 还可以用于读取 CSV 数据并将其插入到 SQL 数据库中。
引入相关依赖
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.7.1</version>
</dependency>
注意:
不同的版本之间可能有的方法没有,较新版本的功能比较多。
创建实体类
@Data
public class User {
@CsvBindByName(column = "姓名")
@CsvBindByPosition(position = 0)
private String name;
@CsvBindByName(column = "年龄")
@CsvBindByPosition(position = 1)
private Integer age;
@CsvBindByName(column = "性别")
@CsvBindByPosition(position = 2)
private String sex;
}
创建Csv工具类
public class CsvUtils {
/**
* csv文件导入并转为java对象
* @param fileName
* @param clazz
* @return
* @param <T>
*/
public static <T> List<T> readCSVFile(String fileName, Class<T> clazz){
try {
FileReader reader = new FileReader(fileName);
CSVReader csvReader = new CSVReader(reader);
HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(clazz);
CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(clsvReader)
.withType(clazz)
//将第一行视为标题行
.withMappingStrategy(strategy)
.build();
return csvToBean.parse();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 将java对象转为csv文件导出
* @param fileName
* @param list
* @return
* @param <T>
*/
public static <T> void writeCSVFile(String fileName, List<T> list) {
try {
FileWriter writer = new FileWriter(fileName);
StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
// 启用标题行
.withOrderedResults(true)
.build();
// 添加标题行(将 Person 对象的字段名作为标题)
writer.write("Name, Age, Sex\n");
beanToCsv.write(list);
writer.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (CsvRequiredFieldEmptyException e) {
throw new RuntimeException(e);
} catch (CsvDataTypeMismatchException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
这里对CSVReader 、CsvToBean、StatefulBeanToCsv等进行详细分析:
CSVReader
CSVReader 是 OpenCSV 库的核心类之一,用于读取 CSV 文件的内容并将其转换为 Java 对象或数据结构。
它支持多种配置选项,可以适应不同的 CSV 文件格式和数据类型。
常用方法:
CsvToBean
CsvToBean 类是 OpenCSV 库的一部分,用于将 CSV 数据解析为 Java 对象或数据结构。
它支持多种配置选项,以适应不同的 CSV 文件格式和数据类型。通常,CsvToBean 被用于将 CSV 行映射到 Java Bean 类,使得 CSV 数据可以更方便地在 Java 应用程序中处理。
常用方法:
parse():解析 CSV 数据并将其映射到 Java 对象。该方法返回一个包含 Java 对象的 List。
parse() 的重载方法:除了简单的 parse()
方法,CsvToBean 还提供了一些重载方法,用于更精细地控制解析过程。
这些方法接受一个
CSVReader
对象作为参数,允许你指定要解析的 CSV 数据源。
withMappingStrategy():设置 CsvToBean 使用的映射策略,用于定义 CSV 行到 Java 对象的映射规则。
通常,你需要创建一个自定义的映射策略类,并将其传递给
withMappingStrategy()
方法。
如何定义映射策略类??见下文等等~~wow~ ⊙o⊙
withType():指定要映射到的目标 Java 类型。
通常,你需要提供目标 Java Bean 类的类对象。
withSeparator():设置 CSV 文件中的分隔符。
默认情况下,分隔符是逗号,但你可以使用该方法来指定其他分隔符。
withQuoteChar():设置用于引用包含特殊字符的字段的字符。
默认情况下,引用字符是双引号,但你可以使用该方法来指定其他引用字符。
withThrowExceptions():设置是否在解析过程中抛出异常。
默认情况下,异常被抑制,但你可以使用该方法来启用异常。
withOrderedResults():指定解析结果是否按照 CSV 文件中的顺序排序。
默认情况下,结果是无序的,但你可以使用该方法来启用排序。
withFilter():设置一个自定义的过滤器,用于过滤解析的数据行。
StatefulBeanToCsv
StatefulBeanToCsv
类是 OpenCSV 库的一部分,用于将 Java 对象或数据结构写入 CSV 文件——比如导出功能。
它支持多种配置选项,以适应不同的 CSV 文件格式和数据类型。通常,StatefulBeanToCsv
被用于将 Java 对象的列表写入 CSV 文件,或者将 Java Bean 对象的属性写入 CSV 行中。
常用方法:
write():将 Java 对象的列表写入 CSV 文件。
这个方法接受一个包含 Java 对象的列表作为参数,将它们写入 CSV 文件。
write() 的重载方法:除了简单的 write()
方法,StatefulBeanToCsv
还提供了一些重载方法,用于更精细地控制写入过程。这些方法允许你指定要写入的 Java 对象列表、字段选择策略等。
其余方法与CsvToBean类相似。
常用的 MappingStrategy 实现(即映射策略)
它将 CSV 文件的列按照它们在文件中的位置与 Java Bean 的属性逐一对应。 例如,CSV 文件的第一列数据将映射到 Java Bean 的第一个属性,第二列数据映射到 Java Bean 的第二个属性,依此类推。
HeaderColumnNameMappingStrategy
CustomMappingStrategy
你可以在自定义策略中定义如何将 CSV 数据映射到 Java Bean,以满足特定的需求。
来,上测试类进行测试吧!
CSV文件解析器工具类
public class CsvUtils {
/**
* csv文件导入并转为java对象
* @param fileName
* @param clazz
* @return
* @param <T>
*/
public static <T> List<T> readCSVFile(String fileName, Class<T> clazz){
try {
FileReader reader = new FileReader(fileName);
CSVReader csvReader = new CSVReader(reader);
HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(clazz);
CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(csvReader)
.withType(clazz)
//将第一行视为标题行
.withMappingStrategy(strategy)
.build();
return csvToBean.parse();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 将java对象转为csv文件导出
* @param fileName
* @param list
* @return
* @param <T>
*/
public static <T> void writeCSVFile(String fileName, List<T> list) {
try {
FileWriter writer = new FileWriter(fileName);
StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
// 启用标题行
.withOrderedResults(true)
.build();
// 添加标题行(将 Person 对象的字段名作为标题)
writer.write("Name, Age, Sex\n");
beanToCsv.write(list);
writer.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (CsvRequiredFieldEmptyException e) {
throw new RuntimeException(e);
} catch (CsvDataTypeMismatchException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
创建映射的实体类,使用注解,定义属性相关的映射关系
@Data
public class User {
@CsvBindByName(column = "姓名")
@CsvBindByPosition(position = 0)
private String name;
@CsvBindByName(column = "年龄")
@CsvBindByPosition(position = 1)
private Integer age;
@CsvBindByName(column = "性别")
@CsvBindByPosition(position = 2)
private String sex;
}
将Java对象转换为csv文件
@Test
void ToCsv(){
User user1 = new User();
user1.setName("Tom");
user1.setAge(22);
user1.setSex("男");
User user2 = new User();
user2.setName("John");
user2.setAge(21);
user2.setSex("女");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
//PATH = "C:\\Users\\16056\\Desktop\\test.csv"
CsvUtils.writeCSVFile(PATH, users);
System.out.println("执行完成");
}
将csv文件转换为Java对象
@Test
void toObject() {
//PATH = "C:\\Users\\16056\\Desktop\\test.csv"
List<User> users = CsvUtils.readCSVFile(PATH, User.class);
for (User user : users){
System.out.println(user);
}
}
导入的 csv 格式文件,文件中的数据在经过 opencsv 解析后,发现读取的第一列里的数据一直为空。 CSV文件:
代码中的映射类:
package com.example.springboot.entity;
import com.opencsv.bean.CsvBindByName;
import lombok.Data;
import java.io.Serializable;
/**
* @Description:
* @title: XxlJobUser
* @Author mhd
* @Date: 2024/11/12 17:25
* @Version 1.0
*/
@Data
public class XxlJobUser implements Serializable {
private static final long serialVersionUID = -8669034503989075986L;
@CsvBindByName(column = "主键")
// @CsvBindByPosition(position = 0)
private Integer id;
@CsvBindByName(column = "姓名")
// @CsvBindByPosition(position = 1)
private String username;
@CsvBindByName(column = "密码")
// @CsvBindByPosition(position = 2)
private String password;
@CsvBindByName(column = "角色")
// @CsvBindByPosition(position = 3)
private Integer role;
@CsvBindByName(column = "权限")
// @CsvBindByPosition(position = 4)
private String permission;
}
排查经过:
主键
列和姓名
列交换位置,发现变成了姓名
列读取的数据为空,主键
列能正常读取到内容。问题现象描述分析:代码逻辑上目前没有问题,导入的数据有的列读取值为空,和CSV的第一列有关。即,CSV文件的第一列在代码中读取时总是为空。
解决方案:
定义一个方法,在opencsv解析文件之间,对文件流进行判断处理
/**
* 读取流中前面的字符,看是否有bom,如果有bom,将bom头先读掉丢弃
*
* @param in
* @return
* @throws IOException
*/
public static InputStream handlerFileBOMHead(InputStream in) throws IOException {
PushbackInputStream testin = new PushbackInputStream(in);
int ch = testin.read();
if (ch != 0xEF) {
testin.unread(ch);
} else if ((ch = testin.read()) != 0xBB) {
testin.unread(ch);
testin.unread(0xef);
} else if ((ch = testin.read()) != 0xBF) {
throw new IOException("错误的UTF-8格式文件");
} else {
}
return testin;
}