本文首發於我的個人網站:
http://riun.xyz
前言
程序中經常使用代碼讀取Excel或Csv文件,每次都要寫繁瑣的讀取代碼,於是我就寫了一個工具類去讀取文件,並將結果儲存爲常用的格式。方便我們快速的拿到結果進行入庫或其他處理。不再將精力浪費在處理文件上。
下面我將全面介紹此工具類。
預備知識
csv文件,一種文本文件格式,以行爲單位儲存數據。每行中的數據以逗號分隔,可以以excel方式打開。
poi,一種java讀取excel文件的工具。可以對行,或特定的單元格進行讀取。
使用
案例:讀取Excel文件
1、準備一個Excel文件,你可以點這兒下載一個( https://github.com/hanhanhanxu/HPoiUtil/blob/master/pleasedownloadme/testexcel.xlsx )【鼠標放在鏈接上,選擇‘鏈接另存爲’】,或者準備你自己的。我的文件中的數據是這樣的:
部門名稱 | 班級名稱 | 學號 | 姓名 | 收費年度 | 總賬欠費金額 | 欠費金額 |
---|---|---|---|---|---|---|
1 | 16軟件工程(JAVA技術應用方向) | 45asdf4w23 | 路飛 | 2019年度 | 13,800.00 | 13,800.00 |
2 | 16軟件工程(JAVA技術應用方向) | 34534sdf34 | 烏索普 | 2019年度 | 5,800.00 | 5,800.00 |
3 | 軟件設計 | 346e5tertw | 索隆 | 2019年度 | 13,800.00 | 13,800.00 |
4 | 軟件設計 | 3456eyerge | 山治 | 2019年度 | 13,800.00 | 13,800.00 |
5 | 軟件設計 | 345e4twset | 娜美 | 2019年度 | 13,800.00 | 13,800.00 |
6 | 軟件設計 | 345634etr2 | 羅賓 | 2019年度 | 13,800.00 | 13,800.00 |
2、準備pojo對象。此對象的含義是,你最終將讀取到的Excel文件中的數據轉化爲什麼對象。比如我excel文件中的數據其實是學生 Student 對象,那麼就新建一個 Student.java。
package riun.xyz.Demo4;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author: HanXu
* on 2019/06/18
* Class description:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private Integer depId;
private String className;
private String sno;
private String name;
private String year;
private Float allAmountArrears;
private Float amountArrears;
private Date createTime;
}
Excel文件中每列都有它的含義,對應着pojo類的屬性。下面是屬性對照關係。需要注意的是,允許存在pojo類中有的屬性,而Excel沒有那一列數據。例如本例中的id,createTime。
3、獲取HPoiUtils.java 的源碼,你可以點這兒下載( https://github.com/hanhanhanxu/HPoiUtil/blob/master/pleasedownloadme/HPoiUtils.java )【鼠標放在鏈接上,選擇‘鏈接另存爲’】。
4、編寫測試案例。
想要讀取Excel文件中的數據,你需要在代碼裏準備一個文件file;一個集合list,此集合中存放的是Excel文件中每列代表含義,即這列映射到pojo中的屬性名字。必選的只有這兩個參數,讓我們試試:
package riun.xyz.nice.Demo1.all;
import org.apache.commons.beanutils.BeanUtils;
import riun.xyz.Demo4.HPoiUtils;
import riun.xyz.Demo4.Student;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
/**
* @author: HanXu
* on 2020/6/18
* Class description: 讀取excel文件的測試
*/
public class Test {
//準備文件file
static final File file = new File("D:\\05HxUtils\\1\\testexcel.xlsx");
//準備屬性集合list
private static List<String> genListField() {
List<String> listField = new ArrayList(12);//建議使用ArrayList,get(i)操作更快
listField.add("depId");
listField.add("className");
listField.add("sno");
listField.add("name");
listField.add("year");
listField.add("allAmountArrears");
listField.add("amountArrears");
return listField;
}
public static void main(String[] args) {
test2();
}
public static void test2() {
List<String> listField = genListField();
//執行
List result = HPoiUtils.exe(file, listField);
for (Object o : result) {
//類格式輸出
Student student = new Student();
try {
BeanUtils.copyProperties(student, o);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(student);
//json格式輸出
/*System.out.println( nice GsonBuilder().setPrettyPrinting().create().toJson(o));
System.out.println();*/
}
System.out.println("共" + result.size() + "條記錄");
}
}
輸出內容爲:
Student(id=null, depId=1, className=16軟件工程(JAVA技術應用方向), sno=45asdf4w23, name=高新傑, year=2019年度, allAmountArrears=13800.0, amountArrears=13800.0, createTime=null)
Student(id=null, depId=2, className=16軟件工程(JAVA技術應用方向), sno=34534sdf34, name=劉德華, year=2019年度, allAmountArrears=5800.0, amountArrears=5800.0, createTime=null)
Student(id=null, depId=3, className=軟件設計, sno=346e5tertw, name=許文瑞, year=2019年度, allAmountArrears=13800.0, amountArrears=13800.0, createTime=null)
Student(id=null, depId=4, className=軟件設計, sno=3456eyerge, name=張奕東, year=2019年度, allAmountArrears=13800.0, amountArrears=13800.0, createTime=null)
Student(id=null, depId=5, className=軟件設計, sno=345e4twset, name=蔣海靖, year=2019年度, allAmountArrears=13800.0, amountArrears=13800.0, createTime=null)
Student(id=null, depId=6, className=軟件設計, sno=345634etr2, name=徐凱, year=2019年度, allAmountArrears=13800.0, amountArrears=13800.0, createTime=null)
共6條記錄
已經成功讀取 Excel文件並將結果轉化爲Student對象,接下來你就可以開心的做其他處理啦。
參數含義
上述代碼只使用一行HPoiUtils.exe就能讀取到文件,拿到結果對象集合了。下面將介紹此方法的幾個參數:
- file:一個Excel文件。必選
- sheetIndex或sheetName:sheetIndex代表sheet索引值,即第幾個sheet,從0開始;sheetName代表sheet名字。可選,默認爲0,即讀取第一個sheet的內容
- list:屬性集合,集合中的元素代表Excel中對應列的意義,一一對應。必選
- class:表示想要轉化成的對象,即將Excel表中的行記錄轉化爲什麼對象。可選,不傳入時會根據傳入list的內容動態生成一個類。大多數時候,建議手動傳入
- hasHeader:Excel中是否有有表頭,true,是,則讀取時不會讀取第一行數據;false,否,當你的Excel文件中沒有表頭,第一行就是數據時,傳入false。可選,默認爲true
共有5個參數,其中兩個是必選的,其他是非必選的。就是說還能使用其他方式讀取,例如:
List result = HPoiUtils.exe(file, listField, false); # 讀取時將認爲Excel文件中沒有表頭,會把第一行也認爲是數據並讀取轉化爲對象。
List result = HPoiUtils.exe(file, 1, listField); # 讀取Excel中的第1個sheet(爲0時可不傳,表示讀取第一個;爲1表示讀取第二個sheet;此處也可傳入sheet名字,不區分大小寫)
List<Student> result = HPoiUtils.exe(file, listField, Student.class); # 讀取到的數據直接就是Student,不用再轉換。通常情況下,建議傳入類。
你也可以選擇5個參數全部手動傳入。
案例:讀取Csv文件
1、準備一個Csv文件,你可以點這兒下載一個【文件testcsv.csv】,或者準備你自己的。我的文件中的數據是這樣的:
id,name,pass,age,sary
1,qweqw,asdfa,12.32
2,45wes,dfgdf,435.309
3,asdt,asdf,345.23
2、準備pojo對象。此對象的含義是,你最終將讀取到的Csv文件中的數據轉化爲什麼對象。比如我csvl文件中的數據其實是員工 Emp 對象,那麼就新建一個 Emp.java。
package riun.xyz.nice.Demo1.all;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: Hanxu
* @Date: 2020/6/18 22:21
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Long id;
private String name;
private String pass;
private Float sary;
private String other;
}
3、獲取HPoiUtils.java 的源碼。
4、編寫測試案例。
想要讀取Csv文件中的數據,你需要在代碼裏準備一個文件file;一個集合list,此集合中存放的是Csv文件中每列代表含義,即這列映射到pojo中的屬性名字。必選的只有這兩個參數,但是我們通常都建議明確傳入class對象,即Emp.class對象,讓我們試試:
package riun.xyz.nice.Demo1.all;
import riun.xyz.Demo5.RepeaterDebtTermDto;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* @author: HanXu
* on 2020/6/16
* Class description: 讀取Csv文件的測試
*/
public class Test {
//準備文件file
static final File file = new File("D:\\05HxUtils\\1\\testcsv.csv");
//準備屬性集合list
private static List<String> genListField() {
List<String> listField = new ArrayList(10);//建議使用ArrayList,get(i)操作更快
listField.add("id");
listField.add("name");
listField.add("pass");
listField.add("sary");
return listField;
}
public static void main(String[] args) {
test1();
}
public static void test1() {
List<String> listField = genListField();
//執行
List<Emp> result = HPoiUtils.exeCsv(file, listField, Emp.class);
for (Emp o : result) {
System.out.println(o);
//System.out.println( nice GsonBuilder().setPrettyPrinting().create().toJson(o));
}
System.out.println(result.size());
}
}
輸出內容爲:
Emp(id=1, name=qweqw, pass=asdfa, sary=12.32, other=null)
Emp(id=2, name=45wes, pass=dfgdf, sary=435.309, other=null)
Emp(id=3, name=asdt, pass=asdf, sary=345.23, other=null)
3
已經得到對象集合數據了,接下來可以進行入庫或者其他處理了。
參數含義
上述代碼仍然是使用一行List<Emp> result = HPoiUtils.exeCsv(file, listField, Emp.class);
即可讀取到文件中的數據並轉化爲對象集合返回。其中參數和上面讀取Excel文件時差不多,只少了一個sheet參數。
下面是參數解釋:
- file:csv文件。必選。
- listField:文件中每列代表的屬性名字集合。必選。
- class:文件中每行對應的對象。可選,不傳入時程序在運行期間會生成一個動態類。
- hasHeader:是否有表頭信息。可選,默認爲true,即有表頭信息。
性能測試
上面讀取到的數據都太小了,我們換一個稍微大點的文件試試:
Excel:125條數據,712ms
Csv:1719條數據,499ms
依賴
<!--poi-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!--javassist.*-->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.2-GA</version>
</dependency>
<!--工具類-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!--pojo類的setter getter等方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.12.6</version>
</dependency>
<!--不傳入類時,類拷貝-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<!--輸出json樣式-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
源碼解釋
本工具處理過程大致分爲如下幾步:
1、參數檢查
2、獲取String結果
3、將String結果對象化
其中第2步,獲取String結果時,根據不同文件,按照給定的sheet進行讀取,讀取時不會判斷是否有表頭,將全部數據讀取,根據傳入的list.size可以進行邏輯分組。
第3步,String對象化時使用了反射,如果傳入有類,就按照類動態生成對象,並根據list中儲存的屬性名字動態執行setter方法,將讀取到的String類型的數據設置進去。
如果沒有傳入類,就動態生成一個類,然後再執行上述。
關於
此工具是我之前HPoiUtils的前身,當時指向讀取Excel文件,後來寫着寫着就加上了讀取Csv文件,更多詳細及歷史版本參見github:https://github.com/hanhanhanxu/HPoiUtil
這個工具類我還會持續更新下去,歡迎給我提各種意見或建議(●’◡’●)