1.CSV介绍
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。
2.maven依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
</dependency>
3.写csv
会自动根据注解识别objectList中的对象值,插入fileHeader对应的列中。objectList中的对象属性上的注解名称一定要对应 fileHeader中的名字。要不然匹配不上,获取不到数据
/**
* 写csv文件 (一次性写 数据不宜过大)
*
* @param objectList 对象
* @param fileHeader 头说明
* @param fileName 文件名称(不要后缀.csv)
* @return File 文件
* @throws BizException 异常
*/
public static File writeCsv(List<Object> objectList, String[] fileHeader, String fileName) {
// 这里显式地配置一下CSV文件的Header,然后设置跳过Header(要不然读的时候会把头也当成一条记录)
CSVFormat format = CSVFormat.DEFAULT.withHeader(fileHeader).withRecordSeparator("\n");
// 这个是定位 判断某个字段的数据应该放在records数组中的那个位子
Map<String, Integer> map = Maps.newHashMap();
for (int i = 0; i < fileHeader.length; i++) {
map.put(fileHeader[i], i);
}
File csvFile = new File(fileName);
try {
// 获取对象的PropertyDescriptor
Map<String, PropertyDescriptor> descriptorMap = null;
// 附加
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "UTF-8"));
CSVPrinter printer = new CSVPrinter(bw, format);
for (Object object : objectList) {
if(CheckUtils.isEmpty(descriptorMap)){
descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(object.getClass());
}
String[] records = new String[fileHeader.length];
for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
if (descriptorMap.containsKey(stringIntegerEntry.getKey())) {
records[map.get(stringIntegerEntry.getKey())] = (String) descriptorMap.get(stringIntegerEntry.getKey()).getReadMethod().invoke(object);
}
}
printer.printRecord(Arrays.asList(records));
}
bw.flush();
bw.close();
printer.close();
} catch (Exception e) {
logger.error("CsvUtils.writeCsv,写csv文件失败,message:{}", e.getMessage(), e);
throw new BizException();
}
return csvFile;
}
4.读取csv
同理可以自动根据注解转换成对象
/**
* 读取csv文件 (一次性读取文件不宜过大)
*
* @param filePath 文件路径
* @param headers csv列头
* @param tClass 返回对象的类型
* @return CSVRecord 列表
* @throws BizException 异常
**/
public static <T> List<T> readCSV(String filePath, String[] headers, Class<T> tClass) {
//创建CSVFormat
CSVFormat format = CSVFormat.DEFAULT.withHeader(headers);
// 获取对象的PropertyDescriptor
List<T> tList = new ArrayList<>();
try {
Map<String, PropertyDescriptor> descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(tClass);
FileReader fileReader = new FileReader(filePath);
//创建CSVParser对象
CSVParser parser = new CSVParser(fileReader, format);
Map<String, Integer> map = parser.getHeaderMap();
for (CSVRecord record : parser) {
T t = tClass.newInstance();
for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
if (descriptorMap.containsKey(stringIntegerEntry.getKey()) && record.size() > stringIntegerEntry.getValue()) {
descriptorMap.get(stringIntegerEntry.getKey()).getWriteMethod().invoke(t, record.get(stringIntegerEntry.getValue()));
}
}
tList.add(t);
}
parser.close();
fileReader.close();
} catch (Exception e) {
logger.error("CsvUtils.readCSV,读取csv文件,message:{}", e.getMessage(), e);
throw new BizException();
}
return tList;
}
5.测试
public static void main(String[] args) throws Exception {
String[] fileHeader = {"name", "sex"};
// 测试写
List<Object> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
MsgResponse msgResponse = new MsgResponse();
msgResponse.setCode("姓名44444888");
msgResponse.setMsg("性别44444488");
list.add(msgResponse);
}
long writeTimeStart = System.currentTimeMillis();
CsvUtils.writeCsv(list, fileHeader, "d:\\workbook.csv");
logger.info("写入时间:" + (System.currentTimeMillis() - writeTimeStart));
// 测试读
long readTimeStart = System.currentTimeMillis();
List<MsgResponse> m = CsvUtils.readCSV("d:\\workbook.csv", fileHeader, MsgResponse.class);
logger.info("读取时间:" + (System.currentTimeMillis() - readTimeStart));
// for (MsgResponse msgResponse : m) {
// logger.info(msgResponse.getCode() + " " + msgResponse.getMsg());
// }
}
结果
生成的csv用excel打开可能会有编码问题,转一下就好了,或者使用文本工具打开就好了
14:46:05.847 [main] INFO CsvUtils.class - 写入时间:593
14:46:07.237 [main] INFO CsvUtils.class - 读取时间:1390
Process finished with exit code 0
文件大概30M左右
csdn源码下载 && 百度云下载直达
完整源码 除了那个注解类 里面就是一个 name字段
package com.store.common.utils;
import com.google.common.collect.Maps;
import com.store.common.annotation.CsvField;
import com.store.common.exception.BizException;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* csv文件读写工具类
*
* @author by YangLD
* @date 2018/7/10
*/
public class CsvUtils {
private static final Logger logger = LoggerFactory.getLogger("CsvUtils.class");
/**
* 写csv文件 (一次性写 数据不宜过大)
*
* @param objectList 对象
* @param fileHeader 头说明
* @param fileName 文件名称(不要后缀.csv)
* @return File 文件
* @throws BizException 异常
*/
public static File writeCsv(List<Object> objectList, String[] fileHeader, String fileName) {
// 这里显式地配置一下CSV文件的Header,然后设置跳过Header(要不然读的时候会把头也当成一条记录)
CSVFormat format = CSVFormat.DEFAULT.withHeader(fileHeader).withRecordSeparator("\n");
// 这个是定位 判断某个字段的数据应该放在records数组中的那个位子
Map<String, Integer> map = Maps.newHashMap();
for (int i = 0; i < fileHeader.length; i++) {
map.put(fileHeader[i], i);
}
File csvFile = new File(fileName);
try {
// 获取对象的PropertyDescriptor
Map<String, PropertyDescriptor> descriptorMap = null;
// 附加
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "UTF-8"));
CSVPrinter printer = new CSVPrinter(bw, format);
for (Object object : objectList) {
if(CheckUtils.isEmpty(descriptorMap)){
descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(object.getClass());
}
String[] records = new String[fileHeader.length];
for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
if (descriptorMap.containsKey(stringIntegerEntry.getKey())) {
records[map.get(stringIntegerEntry.getKey())] = (String) descriptorMap.get(stringIntegerEntry.getKey()).getReadMethod().invoke(object);
}
}
printer.printRecord(Arrays.asList(records));
}
bw.flush();
bw.close();
printer.close();
} catch (Exception e) {
logger.error("CsvUtils.writeCsv,写csv文件失败,message:{}", e.getMessage(), e);
throw new BizException();
}
return csvFile;
}
/**
* 获取对应对象中包含CsvCsvField字段的 PropertyDescriptor
*
* @param tClass 对象的class
* @return Map
* @throws Exception 异常
*/
public static Map<String, PropertyDescriptor> getCsvFieldMapPropertyDescriptor(Class tClass) throws Exception {
Map<String, PropertyDescriptor> descriptorMap = Maps.newHashMap();
BeanInfo beanInfo = Introspector.getBeanInfo(tClass);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获取该字段赋值过来的 字段名称
if (propertyDescriptor.getWriteMethod() == null) {
continue;
}
Field field = tClass.getDeclaredField(propertyDescriptor.getName());
CsvField csvField = field.getAnnotation(CsvField.class);
if (csvField == null) {
continue;
}
String fieldMetaName = csvField.name();
if (CheckUtils.isEmpty(fieldMetaName)) {
continue;
}
descriptorMap.put(fieldMetaName, propertyDescriptor);
}
return descriptorMap;
}
/**
* 读取csv文件 (一次性读取文件不宜过大)
*
* @param filePath 文件路径
* @param headers csv列头
* @param tClass 返回对象的类型
* @return CSVRecord 列表
* @throws BizException 异常
**/
public static <T> List<T> readCSV(String filePath, String[] headers, Class<T> tClass) {
//创建CSVFormat
CSVFormat format = CSVFormat.DEFAULT.withHeader(headers);
// 获取对象的PropertyDescriptor
List<T> tList = new ArrayList<>();
try {
Map<String, PropertyDescriptor> descriptorMap = CsvUtils.getCsvFieldMapPropertyDescriptor(tClass);
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath),"UTF-8");
//创建CSVParser对象
CSVParser parser = new CSVParser(inputStreamReader, format);
Map<String, Integer> map = parser.getHeaderMap();
for (CSVRecord record : parser) {
T t = tClass.newInstance();
for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
if (descriptorMap.containsKey(stringIntegerEntry.getKey()) && record.size() > stringIntegerEntry.getValue()) {
descriptorMap.get(stringIntegerEntry.getKey()).getWriteMethod().invoke(t, record.get(stringIntegerEntry.getValue()));
}
}
tList.add(t);
}
parser.close();
inputStreamReader.close();
} catch (Exception e) {
logger.error("CsvUtils.readCSV,读取csv文件,message:{}", e.getMessage(), e);
throw new BizException();
}
return tList;
}
public static void main(String[] args) throws Exception {
String[] fileHeader = {"姓名","年龄"};
// 测试写
List<Object> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new TargetObject("姓名"+i,""+i+100));
}
long writeTimeStart = System.currentTimeMillis();
CsvUtils.writeCsv(list, fileHeader, "d:\\write.csv");
logger.info("写入时间:" + (System.currentTimeMillis() - writeTimeStart));
// 测试读
long readTimeStart = System.currentTimeMillis();
List<TargetObject> targetObjectList = CsvUtils.readCSV("d:\\write.csv", fileHeader, TargetObject.class);
logger.info("读取时间:" + (System.currentTimeMillis() - readTimeStart));
for (TargetObject targetObject : targetObjectList) {
System.out.println(targetObject.getName() + ": " + targetObject.getAge());
}
}
/** 目标对象 */
public static class TargetObject{
public TargetObject() {
}
public TargetObject(String name, String age) {
this.name = name;
this.age = age;
}
@CsvField(name = "姓名")
private String name;
@CsvField(name = "年龄")
private String age;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}