java基于poi操作Excel

一、环境配置

我们通过学习知道,java操作Excel时需要在maven工程中的pom.xml文件添加poi-ooxml的依赖包,眼下我们选择的版本是3.17,因为相对稳定且使用率较高。配置路径以及结果如下:


二、poi-ooxml源码类学习

我们安装好依赖后按照业务需要,即操作Excel表格(当前不涉及Excel的写入,只有读取操作),会学习到四个源码类,分别是XSSFWorkbook源码类、XSSFSheet源码类、XSSFRow源码类和XSSFCell源码类。学习源码的一个大体节奏是通过源码类先学习类的构造方法,然后是普通方法最后汇总源码方法,进行流水线开发。现在我先把四个源码类的常用方法总结下来,具体如下。

2.1 XSSFWorkbook源码类[public class XSSFWorkbook extends POIXMLDocument implements Workbook]。(路径:poi-ooxml-3.17.jar-->org.apache.poi.xssf.usermodel-->XSSFWorkbook.clss):

//构造方法

public XSSFWorkbook();

public XSSFWorkbook(XSSFWorkbookType workbookType);

// public XSSFWorkbook(OPCPackage pkg) throws IOException;

// public XSSFWorkbook(InputStream is) throws IOException;

public XSSFWorkbook(File file) throws IOException, InvalidFormatException;

---上面构造方法引出File源码类,通过实例话File类拿到File类的对象,传参,获取XSSFWorkbook的对象。

public XSSFWorkbook(String path) throws IOException;

//普通方法

public XSSFSheet cloneSheet(int sheetNum);

public XSSFSheet cloneSheet(int sheetNum, String newName);

public XSSFSheet createSheet();

public XSSFSheet createSheet(String sheetname);

public XSSFSheet getSheet(String name);

public XSSFSheet getSheetAt(int index);

public int getSheetIndex(String name);

public int getSheetIndex(Sheet sheet);

public String getSheetName(int sheetIx);

public void removeSheetAt(int index);

public void setSheetName(int sheetIndex, String sheetname);

2.2 XSSFSheet源码类[public class XSSFSheet extends POIXMLDocumentPart implements Sheet]。上面我们观察到普通方法内创建Sheet、获取Sheet等方法最终都会得到一个sheet对象,那么XSSFSheet源码类又有哪些方法提供给我们使用呢?

// 构造方法

protected XSSFSheet();

protected XSSFSheet(PackagePart part);

注意:XSSFSheet源码类的构造方法都是被保护类型的,不能直接调用,我们可以通过XSSFWorkbook源码类拿到sheet对象,操作XSSFSheet源码类中的方法。

// 普通方法

public XSSFWorkbook getWorkbook();

public String getSheetName();

public int getColumnWidth(int columnIndex);

public int getDefaultColumnWidth();

public boolean isColumnHidden(int columnIndex);

public void setColumnHidden(int columnIndex, boolean hidden);

public void setColumnWidth(int columnIndex, int width);

public void setDefaultColumnWidth(int width)

public int getFirstRowNum();

public int getLastRowNum();

public short getDefaultRowHeight();

public void setDefaultRowHeight(short height);

public XSSFRow createRow(int rownum);

public XSSFRow getRow(int rownum);

public void removeRow(Row row)

2.3 XSSFRow源码类[public class XSSFRow implements Row, Comparable<XSSFRow>]。上面我们可以看到由XSSFWorkbook类对象操作XSSFSheet类中的方法可以得到XSSFRow类的对象。得到行对象后就可以操作XSSFRow源码类中的方法。

// 构造方法

protected XSSFRow(CTRow row, XSSFSheet sheet);

同XSSFSheet源码类,XSSFRow源码类的构造方法也是被保护的,解决方法类似。

// 普通方法

public XSSFSheet getSheet();

public XSSFCell createCell(int columnIndex);

public XSSFCell createCell(int columnIndex, CellType type);

public XSSFCell getCell(int cellnum);

public short getFirstCellNum();

public short getLastCellNum();  // 特殊点:从1开始计数。

public int getRowNum();

public void setRowNum(int rowIndex);

public void removeCell(Cell cell);

2.4 XSSFCell源码类[public final class XSSFCell implements Cell ]。同样的道理,我们来看一下XSSFCell源码类中都有哪些构造方法和普通方法。

// 构造方法

protected XSSFCell(XSSFRow row, CTCell cell);

// 普通方法

public XSSFSheet getSheet();

public XSSFRow getRow();

public boolean getBooleanCellValue();

public void setCellValue(boolean value);

public double getNumericCellValue();

public void setCellValue(double value);

public String getStringCellValue();

public void setCellValue(String str);

public XSSFRichTextString getRichStringCellValue();

public void setCellValue(RichTextString str);

public String getCellFormula();

public void setCellFormula(String formula);

public Date getDateCellValue();

public void setCellValue(Date value);

public String getErrorCellString() throws IllegalStateException;

public void setCellErrorValue(byte errorCode);

public int getColumnIndex();

public int getRowIndex();

public CellType getCellTypeEnum();

三、现在我们已经总结了以上四个源码类中的方法,下面我们就可以写流水线代码了。

import java.io.IOException;

import java.util.Date;

import org.apache.poi.ss.usermodel.CellType;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

//流水线代码

public class TestUtil {

public static void main(String[] args) throws Exception {

XSSFWorkbook wb = new XSSFWorkbook("/Users/xxxxxxxxx/testdata/app_testcase.xlsx");

XSSFSheet getSheet = wb.getSheetAt(3);

XSSFRow getRow = getSheet.getRow(1);

XSSFCell getCell = getRow.getCell(2);

CellType cellType = getCell.getCellTypeEnum();

System.out.println(getSheet.getLastRowNum());

System.out.println(getRow.getFirstCellNum());

System.out.println(getRow.getLastCellNum());

switch(cellType) {

case _NONE:

System.out.println("_NONEvalue为空。");

break;

case NUMERIC:

double numValue = getCell.getNumericCellValue();

System.out.println("numValue = " + numValue);

break;

case STRING:

String strValue = getCell.getStringCellValue();

System.out.println("strValue = " + strValue);

break;

case FORMULA:

String formulaValue = getCell.getCellFormula();

System.out.println("formulaValue = " + formulaValue);

case BLANK:

System.out.println("BLANKvalue为空。");

break;

case BOOLEAN:

boolean booleanValue = getCell.getBooleanCellValue();

System.out.println("booleanValue = " + booleanValue);

break;

case ERROR:

System.out.println("没见过这种格式的单元格。");

break;

default:

Date dateValue = getCell.getDateCellValue();

System.out.println("dateValue = " + dateValue);

break;

}

}

}

四、第一次封装代码。原则上一个源码类我们封装成一个方法,不同的源码类封装成不同的方法。此次封装得到3个方法,分别是:获取workbook、获取sheet、获取单元格的值。

import java.io.IOException;

import java.util.Date;

import org.apache.poi.ss.usermodel.CellType;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

//第一次封装流水线代码

public class TestUtil2 {

//私有属性

private String filePath;

//构造方法

public TestUtil2(String filePath) {

this.filePath = filePath;

}

//普通方法,获取workbook

public XSSFWorkbook getWb() throws Exception {

XSSFWorkbook wb = new XSSFWorkbook(this.filePath);

return wb;

}

//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。

public XSSFSheet getSheet(int sheetIndex) throws Exception {

XSSFWorkbook wb = getWb();

XSSFSheet sheet = wb.getSheetAt(sheetIndex);

return sheet;

}

//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。

public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {

Object value = null;

XSSFSheet sheet = getSheet(sheetIndex);

XSSFRow row = sheet.getRow(rowIndex);

XSSFCell cell = row.getCell(cellIndex);

CellType cellType = cell.getCellTypeEnum();

switch(cellType) {

case _NONE:

value = "";

break;

case NUMERIC:

value = cell.getNumericCellValue();

break;

case STRING:

value = cell.getStringCellValue();

break;

case FORMULA:

value = cell.getCellFormula();

case BLANK:

value = "";

break;

case BOOLEAN:

value = cell.getBooleanCellValue();

break;

case ERROR:

System.out.println("没见过这种格式的单元格。");

break;

default:

value = cell.getDateCellValue();

break;

}

return value;

}

public static void main(String[] args) throws Exception {

TestUtil2 tu2 = new TestUtil2("/Users/xxxxxxx/testdata/app_testcase.xlsx");

Object value = tu2.getCellValue(1, 1, 3);

System.out.println("第2个表格的第二行第四列的值是:" + value);

}

}

五、第二次封装代码。此次封装主要针对上面获取单元格的值做了拆解。由一个方法拆分成两个方法,抽离出直接通过cell对象就能直接返回单元格的值的方法。

import java.io.IOException;

import java.util.Date;

import org.apache.poi.ss.usermodel.CellType;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

//第二次封装流水线代码

public class TestUtil3 {

//私有属性

private String filePath;

//构造方法

public TestUtil3(String filePath) {

this.filePath = filePath;

}

//普通方法,获取workbook

public XSSFWorkbook getWb() throws Exception {

XSSFWorkbook wb = new XSSFWorkbook(this.filePath);

return wb;

}

//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。

public XSSFSheet getSheet(int sheetIndex) throws Exception {

XSSFWorkbook wb = getWb();

XSSFSheet sheet = wb.getSheetAt(sheetIndex);

return sheet;

}

//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。

public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {

XSSFSheet sheet = getSheet(sheetIndex);

XSSFRow row = sheet.getRow(rowIndex);

XSSFCell cell = row.getCell(cellIndex);

Object value = fromCellGetCellValue(cell);

return value;

}

//普通方法,获取单元格的值。通过只传一个cell单元格对象就能返回单元格的值

public Object fromCellGetCellValue(XSSFCell cell) {

Object value = null;

CellType cellType = cell.getCellTypeEnum();

switch(cellType) {

case _NONE:

value = "";

break;

case NUMERIC:

value = cell.getNumericCellValue();

break;

case STRING:

value = cell.getStringCellValue();

break;

case FORMULA:

value = cell.getCellFormula();

case BLANK:

value = "";

break;

case BOOLEAN:

value = cell.getBooleanCellValue();

break;

case ERROR:

System.out.println("没见过这种格式的单元格。");

break;

default:

value = cell.getDateCellValue();

break;

}

return value;

}

public static void main(String[] args) throws Exception {

TestUtil3 tu2 = new TestUtil3("/Users/xxxxxxx/testdata/app_testcase.xlsx");

Object value = tu2.getCellValue(1, 1, 3);

System.out.println("第2个表格的第二行第四列的值是:" + value);

}

}

六、第三次封装代码。第三次封装主要是根据二维数组和Excel表格的相似性(都是通过两个坐标来定位一个元素)来把Excel表格的数据存储到二维数组内,再通过二维数组坐标取值,或者通过两层for循环拿到二维数组的全部值。

import org.apache.poi.ss.usermodel.CellType;

import org.apache.poi.xssf.usermodel.XSSFCell;

import org.apache.poi.xssf.usermodel.XSSFRow;

import org.apache.poi.xssf.usermodel.XSSFSheet;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;

//第三次封装流水线代码

public class TestUtil4 {

//私有属性

private String filePath;

//构造方法

public TestUtil4(String filePath) {

this.filePath = filePath;

}

//普通方法,获取workbook

public XSSFWorkbook getWb() throws Exception {

XSSFWorkbook wb = new XSSFWorkbook(this.filePath);

return wb;

}

//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。

public XSSFSheet getSheet(int sheetIndex) throws Exception {

XSSFWorkbook wb = getWb();

XSSFSheet sheet = wb.getSheetAt(sheetIndex);

return sheet;

}

//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。

public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {

XSSFSheet sheet = getSheet(sheetIndex);

XSSFRow row = sheet.getRow(rowIndex);

XSSFCell cell = row.getCell(cellIndex);

Object value = fromCellGetCellValue(cell);

return value;

}

//普通方法,获取单元格的值。通过只传一个cell单元格对象就能返回单元格的值

public Object fromCellGetCellValue(XSSFCell cell) {

Object value = null;

CellType cellType = cell.getCellTypeEnum();

switch(cellType) {

case _NONE:

value = "";

break;

case NUMERIC:

value = cell.getNumericCellValue();

break;

case STRING:

value = cell.getStringCellValue();

break;

case FORMULA:

value = cell.getCellFormula();

case BLANK:

value = "";

break;

case BOOLEAN:

value = cell.getBooleanCellValue();

break;

case ERROR:

System.out.println("没见过这种格式的单元格。");

break;

default:

value = cell.getDateCellValue();

break;

}

return value;

}

//普通方法,获取整个Excel的数据。通过Excel的坐标定位到cell单元格对象,由上面的fromCellGetCellValue方法得到该单元格的值;再定义一个二维数组接收所有单元格的值。

//注意:二位数组的下表要和excel中想要获取的值的下表对应。

public Object[][] getArrayExcelValue(int sheetIndex) throws Exception{

XSSFSheet sheet =getSheet(sheetIndex);

int lastRowNum = sheet.getLastRowNum();

// int lastCellnum = sheet.getl

Object[][] testData = new Object[lastRowNum][11];

for(int i=1;i<lastRowNum+1;i++) {

XSSFRow row = sheet.getRow(i);

if(row==null) {continue;}

for(int j=row.getFirstCellNum();j<row.getLastCellNum();j++) {

XSSFCell cell = row.getCell(j);

if(cell==null) {continue;}

testData[i-1][j] = fromCellGetCellValue(cell);

// testData[i-1][j] = getCellValue(sheetIndex, i, j);

}

}

return testData;

}

//普通方法,输出二维数组

public static void outPutArray(Object[][] data){

for(int i=0;i<data.length;i++) {

for(int j=0;j<data[i].length;j++) {

Object value = data[i][j];

System.out.print(value + " ");

}

System.out.print("\n");

}

}

public static void main(String[] args) throws Exception {

// TestUtil4 tu2 = new TestUtil4("/Users/zhaoxiaodong/testdata/app_testcase.xlsx");

// Object value = tu2.getCellValue(1, 1, 3);

// System.out.println("第2个表格的第二行第四列的值是:" + value);

TestUtil4 tu4 = new TestUtil4("/Users/zhaoxiaodong/testdata/app_testcase.xlsx");

Object[][] testData = tu4.getArrayExcelValue(3);

// System.out.println(testData);

System.out.println(testData[1][1]);

outPutArray(testData);

}

}

完美,完毕!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容