CsvReader和CsvWriter操作csv文件


使用方法:

  1. 提供把實例數據輸出到磁盤csv文件的功能
  2. 提供讀取csv文件,並封裝成指定實例的功能
  3. 小工具自己依賴了slf4j+logbak,以及fastJson,如果與系統沖突,可以在pom文件中去除
  4. 可以自己手動封裝jar包,引入到自己的工程,也可以復制CsvUtils.java和CsvConfig.java到工程,直接使用

踩的坑:

反射:

使用反射,創建新實例並給各個屬性賦值,獲取屬性值后,調用set方法賦值,因為獲取的值都是String類型,需要根據屬性值類型對值轉型。

 

  • 一開始希望寫一個轉類型方法,根據傳入的Field的類型,把String類型的值轉成屬性值,構造的方法類似:
private <T> T tranform(Field field, String value){
    switch(field.getType().getSimpleName()){
        case "String":
            ...
            return (T)xxx;
        case ...
    }
}                

但是出現一個問題,沒法轉換,一直提示無法把String轉換成Object.后來放棄了

  • 打算使用反射的方式,使用基礎類型的封裝類型中的valueOf方法,思路大致是:
  1. a. 獲取Field的類型全稱
  2. b. 根據全稱,使用Class.forName()獲取對應的Class實例
  3. c. 根據反射,獲取valueOf方法
  4. d. 執行valueOf方法,把String類型的值轉成Field的Type

代碼大致是,手打的,不是真正的代碼:

String typeName = field.getType.getName();
Class<?> typeClass = Class.forName(typeName);
Method valueOf = typeClass.getDeclearMethod("valueOf",java.lang.String);
Object obj = typeClass.newInstence(); //這一步出錯
valueof.invoke(obj,value);	//把要賦予屬性的值,由String類型轉換成屬性類型

但是在獲取封裝類型的實例是,也就是 Object obj = typeClass.newInstence(); 這一步,提示,無法獲取Integer類型的實例,提示是的沒有`<init>`什么的,拋出異常的位置是 C:/Program Files/Java/jdk1.8.0_101/src.zip!/java/lang/Class.java:3082 。后來琢磨了一會,發現只有String類型有無參構造方法,其他封裝類型都沒有無參構造方法,不知道是不是這個原因,String類型可以通過反射獲取實例,其他封裝類型不能獲取實例。

最后放棄掙扎,使用枚舉,使用封裝類型的valueOf方法對值進行轉換。實現方式后面代碼。

1、工具實現代碼

package com.donfaquir.csv;

import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: 
 * @description: 提供csv文件的操作方法
 * @date Create in 2018/12/24 15:01
 * @modified By:
 */
public class CsvUtil {

    private  final Logger log = LoggerFactory.getLogger(CsvUtil.class);

    /**
     * 讀取csc文件
     *
     * @param filePath      文件路徑
     * @param clazz         指定類型
     * @param csvConfig     配置信息
     * @return              讀取結果
     */

    public  <T> List<T> readFile(String filePath, Class<T> clazz, CsvConfig csvConfig) throws Exception {
        if (null == filePath || null == clazz) {
            return null;
        }
        List<T> result = new ArrayList<>(10);
        this.checkCsvConfig(csvConfig);
        CsvReader reader = new CsvReader(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));

        //獲取類方法
        Map<Field, Method> methods = new HashMap<>(11);
        Field[] fields = clazz.getDeclaredFields();
        if (fields == null) {
            log.error("========未獲取到類{}的屬性值,請確認類{}是否傳入正確========", clazz.getName(), clazz.getName());
            return null;
        }
        for (int i = 0; i < fields.length; i++) {
            String methodName = "set" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1);
            Method method = clazz.getMethod(methodName, fields[i].getType());
            if (method == null) {
                log.info("====類{}中沒有屬性{}的set方法,請確定是否實現====", clazz.getName(), fields[i].getName());
                continue;
            }
            methods.put(fields[i], method);
        }
        //跳過頭文件
        reader.readHeaders();
        String[] headers = reader.getHeaders();
        if(null == headers || headers.length <=0){
            log.error("========文件< {} >缺少數據頭,請增加數據頭到文件========",filePath);
            return null;
        }
        while (reader.readRecord()) {
            T obj = (T)clazz.newInstance();
            Set<Field> keys = methods.keySet();
            for (Field key : keys) {
                key.setAccessible(true);
                String value = reader.get(key.getName());
                if(StringUtils.isBlank(value)){
                    continue;
                }
                switch (key.getType().getSimpleName()) {
                    case "Integer":
                        methods.get(key).invoke(obj,Integer.valueOf(value));
                        break;
                    case "String":
                        methods.get(key).invoke(obj,value);
                        break;
                    case "Boolean":
                        methods.get(key).invoke(obj,Boolean.valueOf(value));
                        break;
                    case "Long":
                        methods.get(key).invoke(obj,Long.valueOf(value));
                        break;
                    case "Float":
                        methods.get(key).invoke(obj,Float.valueOf(value));
                        break;
                    case "Double":
                        methods.get(key).invoke(obj,Double.valueOf(value));
                        break;
                    case "Date":
                        try {
                            methods.get(key).invoke(obj,new SimpleDateFormat(csvConfig.getDateFormat()).parse(value));
                            break;
                        } catch (ParseException e) {
                            log.info("====日期轉換失敗,使用的日期格式為:{},與給定的日期數據格式不匹配,請重新指定日期轉換格式====",csvConfig.getDateFormat());
                            log.info("====錯誤原因:{}",e);
                            throw new RuntimeException(e);
                        }
                    default:
                        methods.get(key).invoke(obj,value);
                        break;
                }
            }
            result.add(obj);
        }
        reader.close();
        return result;
    }

    /**
     * 讀取csc文件
     * 默認編碼格式是本地編碼格式
     * @param filePath      文件路徑
     * @param separator     分隔符
     * @param t             指定類型
     * @return              讀取結果
     */
    public  <T> List<T> readFile(String filePath, char separator, Class<T> t) throws Exception {
        return this.readFile(filePath,t,this.initCsvConfig());
    }

    /**
     * 把緩存內容寫入到csv文件
     *
     * @param filePath       文件路徑
     * @param cacheContainer 緩存容器
     * @param csvConfig      配置信息
     * @return 寫入是否成功
     */
    public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t,CsvConfig csvConfig) {
        CsvWriter writer;
        if (StringUtils.isBlank(filePath) || CollectionUtils.isEmpty(cacheContainer) || null == t) {
            return false;
        }
        this.checkCsvConfig(csvConfig);
        writer = new CsvWriter(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));
        //獲取實體類中的屬性
        Field[] fields = t.getDeclaredFields();
        //生成數據頭
        String[] headers = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            headers[i] = fields[i].getName();
        }
        //寫入到文件
        try {
            writer.writeRecord(headers);
            for (T obj : cacheContainer) {
                String[] str = coverFieldsValue(obj,csvConfig);
                writer.writeRecord(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        writer.close();
        return true;
    }

    /**
     * 把緩存內容寫入到csv文件
     * 默認編碼格式是本地編碼格式
     * @param filePath       文件路徑
     * @param cacheContainer 緩存容器
     * @return 寫入是否成功
     */
    public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t) {
        return this.writeFile(filePath,cacheContainer,t,this.initCsvConfig());
    }

    /**
     * 把傳入的實例屬性的值封裝成字符串數組
     *
     * @param obj       實例
     * @return          字符串數組
     * @throws Exception    異常
     */
    private <T>  String[] coverFieldsValue(T obj,CsvConfig csvConfig) throws Exception {
        String[] result ;
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (null == fields || fields.length <= 0) {
            return null;
        }
        result = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            new Date();
            String methodName = "get" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1);
            Method method = clazz.getMethod(methodName);
            Object value = method.invoke(obj);
            if(null == value){
                continue;
            }
            if("Date".equals(fields[i].getType().getSimpleName())){
                Date date = (Date)value;
                result[i] = new SimpleDateFormat(csvConfig.getDateFormat()).format(date);
                continue;
            }
            result[i] = value.toString();
        }
        return result;
    }

    /**
     * 構造一個默認的配置實例
     * 默認編碼格式為本地系統編碼格式
     * @return      設有默認值的配置實例
     */
    private CsvConfig initCsvConfig(){
        String charset = System.getProperty("file.encoding");
        return new CsvConfig(charset,"yyyy-MM-dd HH:mm:ss:SSS",',');
    }

    /**
     * 檢測給予系統配置信息的完整性
     * @param csvConfig        給定的配置實例
     */
    private void checkCsvConfig(CsvConfig csvConfig){
        if(null == csvConfig){
            csvConfig = initCsvConfig();
        }else{
            if(null == csvConfig.getCharset()){
                csvConfig.setCharset(System.getProperty("file.encoding"));
            }
            if(null == csvConfig.getDateFormat()){
                csvConfig.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
            }
            //沒有指定分隔符
            if(0 == csvConfig.getSeparator()){
                csvConfig.setSeparator(',');
            }
        }
    }
}

  

2、參數實體類

用於指定編碼類型、日期格式和分隔符

package com.donfaquir.csv;

/**
 * @author: 
 * @description:    工具配置類
 * @date Create in 2018/12/26 10:09
 * @modified By:
 */
public class CsvConfig {

    /** 字符編碼 */
    private String charset;
    /** 日期格式 */
    private String dateFormat;
    /** 分隔符 */
    private char separator;

    public CsvConfig(){

    }
    public CsvConfig(String charset, String dateFormat, char separator){
        this.charset = charset;
        this.dateFormat = dateFormat;
        this.separator = separator;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public String getDateFormat() {
        return dateFormat;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }

    public char getSeparator() {
        return separator;
    }

    public void setSeparator(char separator) {
        this.separator = separator;
    }
}

  

3、使用demo

import bean.User;
import com.donfaquir.csv.CsvConfig;
import com.donfaquir.csv.CsvUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * @author: 
 * @description:    測試類
 * @date Create in 2018/12/24 15:22
 * @modified By:
 */
public class Demo {
    private  static Logger log = LoggerFactory.getLogger(Demo.class);

    public static void main(String[] args){
        String filePath = System.getProperty("user.home")+"/csv_test.csv";
        CsvConfig csvConfig = new CsvConfig("gbk","yyyy-MM-dd HH:mm:ss:SSS",',');
        CsvUtil csvUtil = new CsvUtil();

        //寫文件代碼
       /* User user = new User("李磊", "男", 14, new Date(), true, 133443L);
        User user1 = new User("jack", "M", 22, new Date(), false, 134L);
        ArrayList<User> users = new ArrayList<>(3);
        users.add(user);
        users.add(user1);
        csvUtil.writeFile(filePath, users, User.class,csvConfig);*/

         //讀文件代碼
        try {
            List<User> objects = csvUtil.readFile(filePath, User.class,csvConfig);
            if(CollectionUtils.isEmpty(objects)){
                log.info("====沒有從文件{}獲取到值====",filePath);
            }
            log.debug("==獲取的結果總數:{}",objects.size());
//            log.debug("==獲取的結果:{}", JSON.toJSONString(objects));
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

  

4、使用中用到的domain類

package bean;

import java.util.Date;

/**
 * @author: 
 * @description:    用戶類
 * @date Create in 2018/12/24 15:21
 * @modified By:
 */
public class User {
    /** 用戶名 */
    private String name;
    /** 性別 */
    private String sex;
    /** 年齡 */
    private Integer age;
    /** 生日 */
    private Date birthday;
    /** 信息是否公開 */
    private Boolean visible;
    /** 過車整形id */
    private Long longId;

    public User() {
        super();
    }

    public User(String name, String sex, Integer age, Date birthday, Boolean visible, Long longId) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthday = birthday;
        this.visible = visible;
        this.longId = longId;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Boolean getVisible() {
        return visible;
    }

    public void setVisible(Boolean visible) {
        this.visible = visible;
    }

    public Long getLongId() {
        return longId;
    }

    public void setLongId(Long longId) {
        this.longId = longId;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM