opencsv4.0 自定義規則
阿新 • • 發佈:2019-02-14
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]