使用Java操作Excel表格
一、配置第三方庫
查閱了網上的資料後,發現Java程序操作Excel表格的庫中使用的比較多的是這個叫Apache POI API
的庫:
對應的maven依賴為:
<!-- //mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
二、使用Apache POI API
1. 打開Excel文件
這一步相當於讀取excel文件中的信息到對象中,方便我們進一步對數據進行操作(比較像Java程序中數據庫映射的POJO對象)。當前我使用的文件是2007以上的,即後綴名為xlxs
,此時創建對象的語句為:
Workbook book = new XSSFWorkbook("xxx.xlxs");
即在構造函數中填入對應的路徑即可。當然,此外還有其他的構造函數,如下圖所示:
但個人覺得最為常用的應該是填入路徑和File
類型的對象了。
2. 選擇對應的sheet
當我們使用Excel時可以發現左下角會有標註各個Sheet,即一個Excel文件像是一本書一樣,裏面有很多個表格,當我們需要操作一個表格時需要先切換到對應的表格頁,如下圖所示:
在程序中我們可以使用WorkBook
的接口函數獲取到一個表格頁(即Sheet):
一共兩種獲取方式:
- 根據Sheet的名稱獲取:
book.getSheet("Sheet1")
- 根據Sheet的索引獲取:
book.getSheetAt(0);
這兩個方法的返回值都是Sheet
接口類型。
3. Sheet接口的基本使用
3.1 獲取開頭行和結束行
在這個系統中,行號是從0開始數的,也就是說在程序中用到的行號是excel表格中顯示的行號-1。
這裡的測試表格採用數據庫課程使用到的數據庫表(即一個簡單的員工數據表),如下圖所示:
獲取開頭行和結束行:
// public interface Sheet
int getFirstRowNum();
int getLastRowNum();
示例程序:
@Test
public void t1() throws IOException {
Workbook book = new XSSFWorkbook("excel/test.xlsx");
Sheet table = book.getSheet("Sheet1");
System.out.println(table.getFirstRowNum());
System.out.println(table.getLastRowNum());
}
測試結果:
即開頭行為第1行,在Java中為0,結束行為第18行,在Java程序中得到17。
3.2 獲取Row對象
Row
對象表示的是數據表中某一行的數據,可以通過以下方式獲取一個Row
對象:
// interface Sheet
Row getRow(int var1);
示例(獲取包含所有第一行的數據的Row
對象):
Row row = table.getRow(0);
4. Row對象的使用
4.1 獲取本行的頭尾索引
① 獲取當前行第一個Cell
對象的索引:row.getFirstCellNum()
tips:
Cell
對象為包含一個單元格所有信息的對象,這裡即為獲取本行第一個非空的單元格的下標
② 獲取當前行最後一個Cell
對象的索引+1:row.getLastCellNum()
注意這裡並不是最後一個Cell對象的索引,而是該值+1(和前面的Sheet.getLastCellNum()
有所不同),例如我們用程序打印出上面員工表第一行的firstCellNum
和lastCellNum
:
@Test
public void t1() throws IOException {
Workbook book = new XSSFWorkbook("excel/test.xlsx");
Sheet table = book.getSheet("Sheet1");
Row row = table.getRow(0);
System.out.println(row.getFirstCellNum());
System.out.println(row.getLastCellNum());
}
運行結果:
而員工表最後一列的索引為6,這說明了getLastCellNum()
這個函數得到的是最後一個Cell的索引+1
對比sheet.getLastRowNum()
和 row.getLastCellNum()
也能發現不同之處(一個是獲取索引,一個是獲取索引+1):
// sheet.getLastRowNum()
public int getLastRowNum() {
return this._rows.isEmpty() ? -1 : (Integer)this._rows.lastKey();
}
// row.getLastCellNum()
public short getLastCellNum() {
return (short)(this._cells.size() == 0 ? -1 : (Integer)this._cells.lastKey() + 1);
}
4.2 獲取Cell對象
一行中有多個Cell
對象(即單元格對象),我們可以通過row.getCell(int index)
獲取Cell
對象,例如我們這裡循環獲取第2行的每一個Cell對象,並且將它們分別打印出來:
Row row = table.getRow(1);
for (int i = 0; i < row.getLastCellNum(); i++) {
Cell cell = row.getCell(i);
System.out.println(cell);
}
Excel第2行:
運行結果:
此外,我們還可以通過迭代器的方式獲取該行的每個Cell對象,獲取迭代器的的方法為row.cellIterator()
,最後將它們打印出來:
Row row = table.getRow(1);
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
System.out.println(cell);
}
運行結果與上同。
閱讀源碼後發現在JDK1.8之後,當一個對象實現了Iterable
接口之後,就已經實現了forEach
方法,因此我們也可以使用這個方法來遍歷Row
對象中的Cell
:
Row row = table.getRow(1);
row.forEach(cell -> {
System.out.print(cell+", ");
System.out.println(cell.getCellType());
});
同理,Sheet
對象也可以使用forEach
進行遍歷。
5. Cell對象的使用
5.1 獲取單元格值的類型
使用方法:
// public interface Cell
CellType getCellType();
返回類型為CellType
,它是一個枚舉類型,源碼如下:
public enum CellType {
_NONE(-1),
NUMERIC(0),
STRING(1),
FORMULA(2),
BLANK(3),
BOOLEAN(4),
ERROR(5);
}
一般Excel中填寫的內容對應的類型:
- 沒有填值(空單元格):
BLANK
- 數字(整數或小數):
NUMERIC
- 字符串:
STRING
- 公式:
FORMULA
- TRUE或FALSE:
BOOLEAN
準備如下的測試行:
執行以下代碼(即將上面這行單元格的值和類型都打印出來):
Row row = table.getRow(18);
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
System.out.print(cell+", ");
System.out.println(cell.getCellType());
}
運行結果:
5.2 獲取單元格的值
方法:cell.getXXXValue()
例如如果這個單元格是數字類型的,就使用getNumericCellValue()
即可
需求,獲取所有SALESMAN
員工的工資和:
5.3 設置單元格的值
和前面獲取值類似地,可以使用:cell.setValue(Object value)
的方法設置單元格的值:
但需要注意的是,這裡僅是對存在於內存中的對象進行屬性的設置,並不會直接影響到Excel表格中實際的內容,如果需要用book對象來修改表格內容則還需要使用保存功能來執行。(和POJO是類似的,需要先修改對象的值,然後再用整個對象去修改數據庫中實際的值)
6. 保存表格
步驟如下:
- 新建一個
FileOutputStream
對象,相當於是一個文件,如果是已存在的文件則進行覆蓋操作 - 使用
Workbook
對象的void write(OutputStream var1)
方法,填入的參數為前面的FileOutputStream
對象
這樣表格就保存完成了。
示例:
@Test
public void t() throws IOException {
Workbook book = new XSSFWorkbook();
Sheet table = book.createSheet();
Row row = table.createRow(0);
Cell firstCell = row.createCell(0);
firstCell.setCellValue("Hello");
Cell secondCell = row.createCell(1);
secondCell.setCellValue("World");
FileOutputStream fos = new FileOutputStream("excel/output.xlsx");
book.write(fos);
book.close();
}
運行效果:
如果希望修改原表格只需在fos的路徑上填原表格就可以進行覆蓋了