0


Excel文件解析

一、前言

java程序开发过程中经常需要使用Excel文件来进行数据的导入与导出,本次文章向大家介绍Excel文件解析或生成以及相关方法

二、Excel文件解析的主流技术:

Apache POI、JXL、Alibaba EasyExcel等。

三、技术特点:

Apache POI:基于DOM方式行进解析,将文件直接加载内存,速度快;

适合场景:较小数据量的Excel文件;

JXL:只支持Excel2003以下的版本(并不常用);

Alibaba EasyExcel:采用逐行读取的解析模式,将每一行的解析结果以观察者的模式通知处理

适合场景:较大数据量的Excel文件;

四、主流技术讲解

Apache POI

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程式对Microsoft Office格式档案读和写的功能。对不同的格式文件提供不同解析:

HSSF:提供读写Microsoft Excel格式档案的功能(用于解析旧版本(*.xls)Excel文件)

注:旧版本Excel文件只能存在65535行数据,所以HSSF目前不常用

XSSF:提供读写Microsoft Excel OOXML格式档案的功能(用于解析新版本(*.xlsx)Excel文件)

HWLF:提供读写Microsoft Work格式档案的功能

HSLF:提供读写Microsoft PowerPoint格式档案的功能

HDGF:提供读写Microsoft Visio格式档案的功能

XSSF解析Excel文件:

使用XSSF解析Excel文件时必须依赖第三方jar包:

使用XSSF解析Excel文件时最重要的四个接口:Workbook(Excel文件)、Sheet(工作簿)、Row(数据行)、Cell(单元格);,并且提供了一系列静态方法

Workbook(Excel文件):

  1. Workbook接口代表一个Excel文件,用于创建或加载(解析)一个Excel文件,常见实现类有:XSSFWorkbook

创建Excel文件:

  1. try (Workbook workbook = new XSSFWorkbook();
  2. FileOutputStream out = new FileOutputStream("D:\\texs\\"+System.currentTimeMillis()+".xlsx")) {
  3. //将Workbook对象中包含的数据,通过输出流,写入至Excel文件
  4. workbook.write(out);
  5. }catch (IOException e) {
  6. e.printStackTrace();
  7. }

注:仅依靠Workbook类虽然可以创建一个Excel文件,但无法打开;

加载(解析)Excel文件:

  1. try (FileInputStream in = new FileInputStream("D:\\琐碎\\1627356552686.xlsx");
  2. Workbook workbook = new XSSFWorkbook(in)) {
  3. } catch (IOException e) {
  4. e.printStackTrace();
  5. }

Sheet(工作簿):

通过Workbook来进行工作簿Sheet对象的创建与获取

获取工作簿:

  1. Sheet sheet=workbook.getSheet("sheet0");
  2. //按照名称获取工作簿
  3. Sheet sheet=workbook.getSheetAt(0);
  4. //按照下标获取工作簿

**创建工作簿: **

  1. Sheet sheet=workbook.createSheet();
  2. //创建默认名称工作簿
  3. Sheet sheet=workbook.createSheet("数据表");
  4. //创建名为“数据表”的工作簿

获取工作簿的数量:

  1. int num=workbook.getNumberOfSheets();

Row(数据行):

通过Sheet来进行数据行Row对象的获取与创建

创建数据行:

  1. Row row=sheet.createRow(0);
  2. //按照下标创建行

获取首尾行下标:

  1. int firstRowIndex=sheet.getFirstRowNum();
  2. //获取首行下标
  3. int lastRowIndex=sheet.getLastRowNum();
  4. //获取尾行下标

根据下标获取指定行:

  1. Row row=sheet.getRow(0);
  2. //获取下标为零的数据行

遍历数据行:

  1. for(Row row:sheet) {
  2. System.out.println(row);
  3. }

遍历指定区域:

  1. for(int i=1;i<sheet.getLastRowNum();i++) {
  2. Row row=sheet.getRow(i);
  3. System.out.println(row);
  4. }

Cell(单元格):

通过Row来进行单元格Cell对象的获取或创建

创建单元格:

  1. Cell cell=row.createCell(0);
  2. //按下标索引来创建单元格

添加单元格值:

  1. Cell cell0=row.createCell(0);
  2. cell0.setCellValue(UUID.randomUUID().toString());
  3. //在当前行下标为0的单元格添加String类型的UUID值
  4. Cell cell1=row.createCell(1);
  5. cell1.setCellValue(Math.PI);
  6. //在当前行下标为1的单元格添加圆周率
  7. Cell cell2=row.createCell(2);
  8. cell2.setCellValue(LocalDateTime.now());
  9. //在当前行下标为2的单元格添加计算机当前时间

根据下标获取单元格:

  1. Cell cell1=row.getCell(0);
  2. //获取当前行下标值为0的单元格
  3. Cell cell2=row.getCell(1);
  4. //获取当前行下标值为1的单元格

遍历所以单元格:

  1. for(Cell cell : Row){
  2. system.out.println(cell);
  3. }

获取单元格类型:

  1. CellType cellType=cell.getCellType();

设置单元格样式:

  1. //获取格式编码值
  2. DataFormat dataFormat=workbook.createDataFormat();
  3. //日期格式编码值
  4. Short dateDataFormat=dataFormat.getFormat("yyyy年MM月dd日 HH:mm:ss SSS");
  5. //货币格式编码值
  6. Short moneyDataFormat=dataFormat.getFormat("¥#,###");
  7. //创建日期格式对象
  8. CellStyle dateStyle=workbook.createCellStyle();
  9. dateStyle.setDataFormat(dateDataFormat);//设置格式编码值
  10. dateStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
  11. dateStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);//垂直居低
  12. //创建货币格式对象
  13. CellStyle moneyStyle=workbook.createCellStyle();
  14. moneyStyle.setDataFormat(moneyDataFormat);//设置格式编码值
  15. moneyStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居低
  16. ....
  17. Cell cell2=row.createCell(2);//日期
  18. cell2.setCellStyle(dateStyle);//日期格式对象
  19. cell2.setCellValue(new Date());
  20. Cell cell3=row.createCell(3);//红包金额
  21. cell3.setCellStyle(moneyStyle);//货币格式对象
  22. cell3.setCellValue((int)(Math.random()*100000));

超大Excel文件读写:

**POI:**​​​​​SXSSFWorkbook类是专门处理大数据的类​,SXSSF是在XSSF的基础上提供了一种支持低内存占用的操作方式;对于大型Excel的创建不会产生内存溢出;

当我们在进行Excel文件操作时,在数据量过大的情况下就可以采用POI提供的SXSSFWorkbook类来避免内存溢出,在使用SXSSFWorkbook进行写入时,首先要设置SXSSFWorkbook的构造参数,这样可以设置每次在内存中保持的行数,当行数达到参数值时,这些在内存中临时保存的数据会被一次性flush(刷新)到磁盘上,这样就不会出现内存不够用的情况,从而达到防止内存溢出的目的;

对POI中的XSSFWorkbook,SXSSFWorkbook和Alibaba中EasyExcel对Excel文件操作的效率进行比较。

使用POI的XSSFWorkbook读取100w行数据:(此操作谨慎尝试!)

  1. public class Demo06 {
  2. public static void main(String[] args) {
  3. String path="D:\\琐碎\\Excel\\100w行数据.xlsx";
  4. long begin=System.currentTimeMillis();
  5. try (Workbook workbook = new XSSFWorkbook(new FileInputStream(path))) {
  6. Sheet sheet=workbook.getSheetAt(0);
  7. //列头
  8. Row headRow =sheet.getRow(0);
  9. //遍历列头单元格
  10. for(Cell headCell:headRow) {
  11. System.out.print(headCell+"\t");
  12. }
  13. System.out.println();
  14. //数据行
  15. for(int i=1;i<sheet.getLastRowNum();i++) {
  16. //获取当前行
  17. Row row=sheet.getRow(i);
  18. for (Cell cell:row) {
  19. CellType cellType=cell.getCellType();
  20. switch (cellType) {
  21. case STRING:
  22. System.out.print(cell.getStringCellValue()+"\t");
  23. break;
  24. case NUMERIC:
  25. System.out.print(cell.getNumericCellValue()+"\t");
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. System.out.println();
  32. }
  33. long end=System.currentTimeMillis();
  34. System.out.println("程序共运行:"+(end-begin)/1000.0+"秒");
  35. } catch (IOException e) {
  36. // TODO Auto-generated catch block
  37. e.printStackTrace();
  38. }
  39. }
  40. }

运行结果:

使用POI的SXSSFWorkbook写入100w行数据:

  1. public class Demo {
  2. public static void main(String[] args) {
  3. long startTime=System.currentTimeMillis();
  4. try (Workbook workbook = new SXSSFWorkbook(200);
  5. FileOutputStream fos = new FileOutputStream("D:\\琐碎\\Excel\\100w行数据.xlsx")) {
  6. Sheet sheet1 = workbook.createSheet();
  7. for (int i = 0; i <= 1000000; i++) {
  8. Row row = sheet1.createRow(i);
  9. Cell cell0 = row.createCell(0);
  10. cell0.setCellValue(UUID.randomUUID().toString());
  11. Cell cell1 = row.createCell(1);
  12. cell1.setCellValue(LocalDateTime.now());
  13. }
  14. workbook.write(fos);
  15. long endTime=System.currentTimeMillis();
  16. System.out.println("使用SXSSFWorkbook写入100w行数据需:"+(endTime-startTime)/1000.0+"秒");
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

写入前CPU与内存的占用情况:

写入时CPU与内存的占用情况:

代码运行结果:

  1. 使用SXSSFWorkbook写入100w行数据需:12.645

Alibaba EasyExcel:

EasyExcel是一个基于Java的操作简单、节省内存的读写Excel的Alibaba开源项目。在尽可能节约内存的情况下支持读写超大数据的Excel。

同样,要使用Alibaba EasyExcel必须下载相关jar包,并且不可随意下载不同版本:

准备实体类:

实体订单类:

  1. import java.time.LocalDateTime;
  2. import java.time.format.DateTimeFormatter;
  3. import java.util.UUID;
  4. import com.alibaba.excel.annotation.ExcelProperty;
  5. import com.alibaba.excel.annotation.format.NumberFormat;
  6. public class Order {
  7. @ExcelProperty("订单编号")
  8. private String orderId;
  9. @ExcelProperty("支付金额")
  10. @NumberFormat("¥#,###")
  11. private double money;
  12. @ExcelProperty(value="创建日期",converter = LocalDateTimeConveter.class)
  13. private LocalDateTime createTime;
  14. public Order() {
  15. this.orderId=LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss"))+UUID.randomUUID().toString().substring(0,5);
  16. this.money=Math.random()*100000;
  17. this.createTime=LocalDateTime.now();
  18. }
  19. public String getOrderId() {
  20. return orderId;
  21. }
  22. public void setOrderId(String orderId) {
  23. this.orderId = orderId;
  24. }
  25. public double getMoney() {
  26. return money;
  27. }
  28. public void setMoney(double money) {
  29. this.money = money;
  30. }
  31. public LocalDateTime getCreateTime() {
  32. return createTime;
  33. }
  34. public void setCreateTime(LocalDateTime createTime) {
  35. this.createTime = createTime;
  36. }
  37. @Override
  38. public String toString() {
  39. return "Order [orderId=" + orderId + ", money=" + money + ", createTime=" + createTime + "]";
  40. }
  41. }

Converter转换类:目的---兼容LocalDateTime日期时间类

  1. import java.time.LocalDateTime;
  2. import java.time.format.DateTimeFormatter;
  3. import com.alibaba.excel.converters.Converter;
  4. import com.alibaba.excel.enums.CellDataTypeEnum;
  5. import com.alibaba.excel.metadata.CellData;
  6. import com.alibaba.excel.metadata.GlobalConfiguration;
  7. import com.alibaba.excel.metadata.property.ExcelContentProperty;
  8. public class LocalDateTimeConveter implements Converter<LocalDateTime> {
  9. @Override
  10. public CellData<String> convertToExcelData(LocalDateTime arg0, ExcelContentProperty arg1, GlobalConfiguration arg2)
  11. throws Exception {
  12. // TODO Auto-generated method stub
  13. return new CellData<>(arg0.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  14. }
  15. @Override
  16. public LocalDateTime convertToJavaData(CellData arg0, ExcelContentProperty arg1, GlobalConfiguration arg2)
  17. throws Exception {
  18. // TODO Auto-generated method stub
  19. return LocalDateTime.parse(arg0.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  20. }
  21. @Override
  22. public CellDataTypeEnum supportExcelTypeKey() {
  23. // TODO Auto-generated method stub
  24. return CellDataTypeEnum.STRING;
  25. }
  26. @Override
  27. public Class supportJavaTypeKey() {
  28. // TODO Auto-generated method stub
  29. return LocalDateTime.class;
  30. }
  31. }

使用EasyExcel写入100w条数据:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import com.alibaba.excel.EasyExcel;
  4. import com.jaiojaio.entity.Order;
  5. public class Demo01 {
  6. public static void main(String[] args) {
  7. // 写入100w
  8. long startTime=System.currentTimeMillis();
  9. EasyExcel.write("D:\\琐碎\\Excel\\100weasy.xlsx", Order.class)
  10. .sheet("订单列表")
  11. .doWrite(data());
  12. long endTime=System.currentTimeMillis();
  13. System.out.println("使用EasyExcel写入100w条数据需:"+(endTime-startTime)/1000.0+"秒");
  14. }
  15. // 创建100w条订单数据
  16. private static List<Order> data() {
  17. List<Order> list = new ArrayList<Order>();
  18. for (int i = 0; i < 1000000; i++) {
  19. list.add(new Order());
  20. }
  21. return list;
  22. }
  23. }

写入时CPU与内存的占用情况:

运行结果:

  1. SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
  2. SLF4J: Defaulting to no-operation (NOP) logger implementation
  3. SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
  4. 使用EasyExcel写入100w条数据需:15.19

使用EasyExcel读取100w条数据:

  1. import java.util.LinkedList;
  2. import java.util.List;
  3. import java.util.Map;
  4. import com.alibaba.excel.EasyExcel;
  5. import com.alibaba.excel.context.AnalysisContext;
  6. import com.alibaba.excel.event.AnalysisEventListener;
  7. import com.jaiojaio.entity.Order;
  8. public class Demo02 {
  9. public static void main(String[] args) {
  10. long begin=System.currentTimeMillis();
  11. List<Order> list=new LinkedList<Order>();
  12. EasyExcel.read("D:\\琐碎\\Excel\\100weasy.xlsx", Order.class, new AnalysisEventListener<Order>() {
  13. @Override
  14. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
  15. // TODO Auto-generated method stub
  16. System.out.println(headMap);
  17. super.invokeHeadMap(headMap, context);
  18. }
  19. @Override
  20. public void invoke(Order arg0, AnalysisContext arg1) {
  21. // TODO Auto-generated method stub
  22. list.add(arg0);
  23. }
  24. @Override
  25. public void doAfterAllAnalysed(AnalysisContext arg0) {
  26. // TODO Auto-generated method stub
  27. System.out.println("game over");
  28. }
  29. }).sheet().doRead();
  30. for(Order order:list) {
  31. System.out.println(order);
  32. }
  33. long end=System.currentTimeMillis();
  34. System.out.println("程序需运行:"+(end-begin)/1000.0+"秒");
  35. }
  36. }

读取时CPU与内存的占用情况:

代码运行结果:

  1. ....
  2. Order [orderId=20220717 11:41:37e9797, money=86139.0, createTime=2022-07-17T11:41:37]
  3. Order [orderId=20220717 11:41:377f2bc, money=52157.0, createTime=2022-07-17T11:41:37]
  4. Order [orderId=20220717 11:41:37afbb1, money=2815.0, createTime=2022-07-17T11:41:37]
  5. Order [orderId=20220717 11:41:37db079, money=27125.0, createTime=2022-07-17T11:41:37]
  6. Order [orderId=20220717 11:41:37944f7, money=92808.0, createTime=2022-07-17T11:41:37]
  7. Order [orderId=20220717 11:41:37862af, money=90109.0, createTime=2022-07-17T11:41:37]
  8. Order [orderId=20220717 11:41:378d5fe, money=41899.0, createTime=2022-07-17T11:41:37]
  9. Order [orderId=20220717 11:41:3743530, money=4537.0, createTime=2022-07-17T11:41:37]
  10. 程序需运行:23.418

综上所属,在操作超大数据量的Excel文件时,不应使用操作普通文件的XSSFWork进行操作,应使用SXSSFWork或EasyExcel,并且EasyExcel的操作更为简单,因此,对于不同的需求进行选择即可

标签: eclipse java

本文转载自: https://blog.csdn.net/w259149/article/details/125766078
版权归原作者 雾远望 所有, 如有侵权,请联系我们删除。

“Excel文件解析”的评论:

还没有评论