java 读取和写入csv文件 (自动提取和转换成对象数据)

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;
        }
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值