1. 程式人生 > >opencsv4.0 自定義規則

opencsv4.0 自定義規則

opencsv

java讀取csv的類庫主要有兩種,opencsv和javacsv,研究發現,javacsv最後一次更新是2014-12-10,很久不維護了。opencsv是apache的專案,並且至今仍在維護,所以決定使用opencsv。

csv

csv檔案,全名 comma separated values,預設以逗號分隔,是純文字檔案。雖然用excel開啟後格式排版了,但是那是excel對他進行了處理。用notepad或者sublime text開啟能看到最原始的文字。 
為了後續舉例,這裡編輯了一個test.csv

header1,header2,header3
1,a,10
2,b,20
3,c,30
4
,d,40 5,e,50 6,f,60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

讀取方式

  • 最原始的方式,逐行讀取,然後操作
 CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));

      /*
       * 逐行讀取
       */
      String[] strArr = null;
      while((strArr = reader.readNext())!=null){
          System.out.println(strArr[0]+"---"+strArr[1
]+"----"+strArr[2]); } reader.close();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

繫結csv檔案轉換成bean

逐行讀取操作是最原始的操作方式,opencsv提供了基於“策略”的對映,將csv繫結到bean。

策略簡介

觀察一下策略的繼承層次 
這裡寫圖片描述

介面

  • MappingStrategy 
    • 對映的頂層介面
  • HeaderColumnNameMappingStrategy 
    • 列名對映策略,讀取csv檔案第一行作為header,比如header1,header2,header3,然後呼叫bean的setHeader1方法,setHeader2方法,setHeader3方法分別設定值,所以這種策略要求,列名與bean中的屬性名完全一致,如果不一致,則值為空,不會出錯。使用註解時,註解名字必須與csv中列名一致。
  • ColumnPositionMappingStrategy 
    • 列位置對映策略,他沒有header的概念,所以會輸出取所有行。在columnMapping陣列中指定bean的屬性,第一個值對應csv的第一列,第二個值對應csv的第二類……
  • HeaderColumnNameTranslateMappingStrategy 
    • 列頭名字翻譯對映策略,與HeaderColumnNameMappintStrategy相比,bean的屬性名可以與csv列頭不一樣。通過指定map來對映。

具體對映用法

Java POJO類

public class SimpleBeanInfo {

    private String header1;


    private String header2;

    private String header3;

    public String getHeader1() {
        return header1;
    }

    @Override
    public String toString() {
        return "SimpleBeanInfo [header1=" + header1 + ", header2=" + header2
                + ", header3=" + header3 + "]";
    }

    public void setHeader1(String header1) {
        this.header1 = header1;
    }

    public String getHeader2() {
        return header2;
    }

    public void setHeader2(String header2) {
        this.header2 = header2;
    }

    public String getHeader3() {
        return header3;
    }

    public void setHeader3(String header3) {
        this.header3 = header3;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

基於列索引的對映

  • 通俗點來說就是列位置對映,csv檔案中的列位置對應到bean中的列

  • 非註解方式

CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));
      /*
       *基於列位置,對映成類 
       */
       //csv檔案中的第一列對應類的header,第二列對應類的header2,第三列對應類的header3
      String[] columnMapping={"header1","header2","header3"};
      ColumnPositionMappingStrategy<SimpleBeanInfo> mapper = new ColumnPositionMappingStrategy<SimpleBeanInfo>();
      mapper.setColumnMapping(columnMapping);
      mapper.setType(SimpleBeanInfo.class);
      /* */ 
      CsvToBean<SimpleBeanInfo>  csvToBean = new CsvToBean<SimpleBeanInfo>();

      List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader);

      for(SimpleBeanInfo e : list){
          System.out.println(e);
      }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 註解方式
public class SimpleBeanInfo {

    @CsvBindByPosition(position=0)
    private String header1;


    @CsvBindByPosition(position=1)
    private String header2;

    @CsvBindByPosition(position=2)
    private String header3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));
 ColumnPositionMappingStrategy<SimpleBeanInfo> mapper = new ColumnPositionMappingStrategy<SimpleBeanInfo>();
      mapper.setType(SimpleBeanInfo.class);
      CsvToBean<SimpleBeanInfo>  csvToBean = new CsvToBean<SimpleBeanInfo>();

      List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader);

      for(SimpleBeanInfo e : list){
          System.out.println(e);
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

基於列名的對映

  • 非註解方式
 CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));

      /* */ 

      HeaderColumnNameMappingStrategy<SimpleBeanInfo> mapper = new
              HeaderColumnNameMappingStrategy<SimpleBeanInfo>();
      mapper.setType(SimpleBeanInfo.class);
      CsvToBean<SimpleBeanInfo>  csvToBean = new CsvToBean<SimpleBeanInfo>();

      List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader);

      for(SimpleBeanInfo e : list){
          System.out.println(e);
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 註解方式
public class SimpleBeanInfo {

    @CsvBindByName(column="header1")
    private String header1;


    @CsvBindByName(column="header2")
    private String header2;

    @CsvBindByName(column="header3")
    private String header3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));

      HeaderColumnNameMappingStrategy<SimpleBeanInfo> mapper = new
              HeaderColumnNameMappingStrategy<SimpleBeanInfo>();
      mapper.setType(SimpleBeanInfo.class);
      CsvToBean<SimpleBeanInfo>  csvToBean = new CsvToBean<SimpleBeanInfo>();

      List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader);

      for(SimpleBeanInfo e : list){
          System.out.println(e);
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

基於列名轉換對映

 CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream("test.csv"),"gbk"));
      /*
       * 基於列名轉換,對映成類
      */
      HeaderColumnNameTranslateMappingStrategy<SimpleBeanInfo> mapper = 
              new HeaderColumnNameTranslateMappingStrategy<SimpleBeanInfo>();
      mapper.setType(SimpleBeanInfo.class);

      Map<String,String> columnMapping = new HashMap<String,String>();
      columnMapping.put("header1", "header1");//csv中的header1對應bean的header1
      columnMapping.put("header2", "header2");
      columnMapping.put("header3", "header3");
      mapper.setColumnMapping(columnMapping);

      CsvToBean<SimpleBeanInfo>  csvToBean = new CsvToBean<SimpleBeanInfo>();

      List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader);

      for(SimpleBeanInfo e : list){
          System.out.println(e);
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

過濾器

opencsv提供了過濾器,可以過濾某些行,比如page header、page footer等

  • 所有的過濾器必須實現CsvToBeanFilter 介面
public class MyCsvToBeanFilter implements CsvToBeanFilter {

        public boolean allowLine(String[] line) {
            //過濾第一列值等於1的行
            if("1".equals(line[0])){
                return false;
            }
            return true;
        }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
 MyCsvToBeanFilter filter = new MyCsvToBeanFilter();
 List<SimpleBeanInfo> list = csvToBean.parse(mapper, reader,filter);
  • 1
  • 2

轉化器

類中的屬性不一定都是字串,比如數字、日期等,但是我們從csv中獲取到的都是字串,這種情況就應該使用轉化器。 
這裡定義一個SimpleBeanConverter,繼承AbstractBeanField


public class SimpleBeanFieldConverter extends AbstractBeanField<SimpleBeanInfo> {

    @Override
    protected Object convert(String value) throws CsvDataTypeMismatchException,
            CsvRequiredFieldEmptyException, CsvConstraintViolationException {
        Field f = getField();
        if("date".equals(f.getName())){
            try {
                return new SimpleDateFormat("yyyy-MM-dd").parse(value);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

test.csv新增一列header4

header1,header2,header3,header4
1,a,10,2016-05-01
2,b,20,2016-05-02
3,c,30,2016-05-03
4,d,40,2016-05-04
5,e,50,2016-05-05
6,f,60,2016-05-06
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

SimpleBeanInfo新增屬性

@CsvCustomBindByPosition(position=3,converter=SimpleBeanFieldConverter.class)
private Date date;
  • 1
  • 2

輸出結果 
由於ColumnPositionMappingStrategy會連header行也解析,所以第一行會列印異常資訊。我們看到header4列已經轉換為日期。如果不只一個列需要轉換怎麼辦?在相應的屬性上添加註解(@CsvCustomBindByPosition或@CsvCustomBindByName),然後在convert(Object value)中擴充套件即可

java.text.ParseException: Unparseable date: "header4"
    at java.text.DateFormat.parse(DateFormat.java:357)
    at test_maven.SimpleBeanFieldConverter.convert(SimpleBeanFieldConverter.java:24)
    at com.opencsv.bean.AbstractBeanField.setFieldValue(AbstractBeanField.java:70)
    at com.opencsv.bean.CsvToBean.processField(CsvToBean.java:245)
    at com.opencsv.bean.CsvToBean.processLine(CsvToBean.java:220)
    at com.opencsv.bean.CsvToBean.processLine(CsvToBean.java:189)
    at com.opencsv.bean.CsvToBean.parse(CsvToBean.java:166)
    at com.opencsv.bean.CsvToBean.parse(CsvToBean.java:133)
    at test_maven.TestCSV.main(TestCSV.java:46)
SimpleBeanInfo [header1=header1, header2=header2, header3=header3, date=null]
SimpleBeanInfo [header1=2, header2=b, header3=20, date=Mon May 02 00:00:00 CST 2016]
SimpleBeanInfo [header1=3, header2=c, header3=30, date=Tue May 03 00:00:00 CST 2016]
SimpleBeanInfo [header1=4, header2=d, header3=40, date=Wed May 04 00:00:00 CST 2016]
SimpleBeanInfo [header1=5, header2=e, header3=50, date=Thu May 05 00:00:00 CST 2016]
SimpleBeanInfo [header1=6, header2=f, header3=60, date=Fri May 06 00:00:00 CST 2016]