Excel文件解析--超大Excel文件读写

使用POI写入

当我们想在Excel文件中写入100w条数据时,我们用普通的XSSFWorkbook对象写入时会发现,只有在将100w条数据全部加载入内存后才会用write()方法统一写入,这样效率很低,所以我们引入了SXSSFWorkbook进行超大Excel文件的读写。

SXSSFWorkbook可以通过构造参数来控制:当数据写入内存量达到参数值时,就把这些数据flush到Excel文件中

java 复制代码
public class Demo02_SXSSFWorkbook {
    public static void main(String[] args) {
        String Path="D://IO流//0421.xlsx";
        try (Workbook workbook = new SXSSFWorkbook(1000);
            FileOutputStream out=new FileOutputStream(Path)){
                //生成Sheet
                Sheet sheet=workbook.createSheet();
                for(int i=0;i<1000000;i++) {
					Row row =sheet.createRow(i);
					Cell cell0=row.createCell(0);
					cell0.setCellValue(UUID.randomUUID().toString());
					
					Cell cell1=row.createCell(1);
					cell1.setCellValue(new Date());
				}
            //写入输出流
			workbook.write(out);
        }catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

使用EasyExcel写入

使用EasyExcel,我们首先要导入相关jar包

这是一个普通的Order类:

java 复制代码
public class Order {
    private String orderId;

	private Double payment;

    public Order() {
		this.orderId=LocalDateTime.now().
           format(DateTimeFormatter.ofPattern(
           "yyyyMMddHHmmss"))+UUID.randomUUID()
           .toString().substring(0,5);
		this.payment=Math.random()*1000;

	}

	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public Double getPayment() {
		return payment;
	}

	public void setPayment(Double payment) {
		this.payment = payment;
	}
	@Override
	public String toString() {
		return "Order [orderId=" + orderId + ", payment=" 
                                                + payment + "]";
	}
}

然后我们来通过EasyExcel来将100w条数据写入excel文件:

java 复制代码
public class Text_order {
    public static void main(String[] args) {
        EasyExcel.write("D://IO流//422.xlsx",Order.class)
                 .sheet("订单数据")
                 .dowrite(creatOrderData());
    }
    //生成100w条数据
    private static List<Order> creatOrderData(){
        List<Order> orderList=new ArrayList<Order>();
        for(int i=0;i<1000000;i++) {
            orderList.add(new Order());
        }
        return orderList;
    }
}

运行结果:

我们发现,Order类中的成员变量名就是我们生成的Excel文件中的列头。那么如果我们想自定义列头时,我们可以用:@ExcelProperty("列头名")

java 复制代码
public class Order {
    @ExcelProperty("订单编号")
    private String orderId;
    
    @ExcelProperty("支付金额")
	private Double payment;

    public Order() {
    ...
    }
}

运行结果:

那么,当我们想加入一列日期数据时:

java 复制代码
public class Order {
    @ExcelProperty("订单编号")
	private String orderId;
	
	@ExcelProperty("支付金额")
	private Double payment;

	@ExcelProperty("创建时间")
	private LocalDateTime  creatTime;
    public Order() {
		this.orderId=LocalDateTime.now().
           format(DateTimeFormatter.ofPattern(
           "yyyyMMddHHmmss"))+UUID.randomUUID()
           .toString().substring(0,5);
		this.payment=Math.random()*1000;
		this.creatTime=LocalDateTime.now();
	}

	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public Double getPayment() {
		return payment;
	}

	public void setPayment(Double payment) {
		this.payment = payment;
	}
    
    public LocalDateTime getCreatTime() {
		return creatTime;
	}

	public void setCreatTime(LocalDateTime creatTime) {
		this.creatTime = creatTime;
	}
	@Override
	public String toString() {
		return "Order [orderId=" + orderId + ", payment=" + payment
                                 + ", creatTime=" + creatTime + "]";
	}
}

运行结果:

通过阅读报错提示(Can not find 'Converter' support class LocalDateTime.) ,我们大概可以知道,是因为找不到一个支持LocalDateTime类的转换器,所以为了解决这个问题,我们可以自己写一个比较器类:

java 复制代码
public class LocalDateTimeConverter 
                        implements Converter<LocalDateTime> {
    //Excel文件中的类型
	@Override
	public CellDataTypeEnum supportExcelTypeKey() {
		// TODO Auto-generated method stub
		return CellDataTypeEnum.STRING;
	}
	//程序中的类型
	@Override
	public Class supportJavaTypeKey() {
		// TODO Auto-generated method stub
		return LocalDateTime.class;
	}

    //将LocalDateTime类型的数据转换成String
	//并封装到一个Excel文件中的CellData
    @Override
	public CellData convertToExcelData(LocalDateTime value,         
                          ExcelContentProperty arg1,
                          GlobalConfiguration arg2)throws Exception {
		// TODO Auto-generated method stub
		return new CellData<>(
				          value.format(DateTimeFormatter.ofPattern
                          ("yyyy年MM月dd日 HH:mm:ss")));
	}

    //从CellData中获取一个String类型的数据
	//并转换成LocalDateTime
	@Override
	public LocalDateTime convertToJavaData(CellData cellData,             
                         ExcelContentProperty arg1,
                         GlobalConfiguration arg2)throws Exception {
		// TODO Auto-generated method stub
		return LocalDateTime.parse(				            
                         cellData.getStringValue(),
                         DateTimeFormatter.ofPattern
                         ("yyyy年MM月dd日 HH:mm:ss"));
	}
}

当我们写好这个比较器后,就需要给成员变量creatTime显示的设置好比较器:

java 复制代码
public class Order {
    @ExcelProperty("订单编号")
	private String orderId;
	
	@ExcelProperty("支付金额")
	private Double payment;

	//设置LocalDateTime对应转换器
	@ExcelProperty(value="创建时间",
                             converter=LocalDateTimeConverter.class)
	private LocalDateTime  creatTime;
    public Order() {
        ...
    }
}

运行结果:

Excel文件解析的应用

案例一:检查Excel文件

//检查demo-data.xlsx文件中的人员信息格式是否正确,具体要求如下:

// 1.序号是否连续

// 2.检查性别是否为男或女

// 3.身份证号

// 3.1 身份证号码格式(必须为18位)

// 3.2 身份证号码不能重复

// 3.3 身份证号码开头两位是否与籍贯符合

// 北京 11 天津12 河北 13 山西14 内蒙古 15

// 陕西61 甘肃62 青海 63

// 4.学历只能填写:大专、本科、硕士、其它

// 5.体重在40-180之间

java 复制代码
public class Work01 {
    public static void main(String[] args) {
    List<String> errorMsgList=validateDataExcel("D:\\IO流\\demo-    
                                                     data.xlsx");
    if(errorMsgList.size()==0){
        System.out.println("文件检查无误");
    }else {
			//显示错误信息
			for(String err:errorMsgList) {
				System.out.println(err);
			}
		}
	}
    public static List<String> validateDataExcel(String path){
        //创建一个list用于保存错误提示信息
        ArrayList<String> errorList=new ArrayList<String>();

        //创建一个set用于检查身份证账号是否重复的集合
        HashSet<String> idcardNoSet=new HashSet<String>();

        //创建一个HashMap用于检查身份证号码开头两位是否与籍贯符合
        HashMap<String,String> provinceMap=
                                    new HashMap<String,String>(){
            //匿名构造代码块
            {
				put("11","北京");
				put("12","天津");
				put("13","河北");
				put("14","山西");
				put("15","内蒙古");
				put("61","陕西");
				put("62","甘肃");
				put("63","青海");
			}
        };

        //创建一个用于检查学历的list
        List<String> eduList=Arrays.asList(
                                        "大专","硕士","本科","其他");
        try(Workbook workbook=new XSSFWorkbook(path)){
            Sheet sheet=workbook.getSheetAt(0);
            
            //获取每个Cell中的数据
            for(int i =1;i<sheet.getLastRowNum();i++){
                
            //1.序号是否连续
            Cell cellId=row.getRow(0);
            //通过当前行数获取正确的序号
            int rowNum=row.getRowNum();
            //获取文件中的序号
            int id=(int)cellId.getNumericCellValue();
            if(rowNum!=id) {
					errorList.add(String.format("%d行的数据
                                                    不连续",rowNum));		
			}

            //2.检查性别是否为男或女
            String gender =row.getCell(2).getStringCellValue();
            if(!ender.equals("男")&&!ender.equals("女")){
                errorList.add(String.format("%d行的性别有误",rowNum));
            }

            // 3.身份证号
            String idCardNo=row.getCell(3).getStringCellValue();
            // 3.1 身份证号码格式(必须为18位)
            if(idCardNo.length()!=18) {
                errorList.add(String.format("%d行的身份证号码
                                                   长度有误",rowNum));
		    }
            // 3.2 身份证号码不能重复
			//如果成功添加进idcardNoSet集合,说明该身份证号没有重复
            //如果添加不成功,代表有重复
            if(!idcardNoSet.add(idCardNo)){
                errorList.add(String.format("%d行的身份证号码
                                                      重复",rowNum));
			}
            // 3.3 身份证号码开头两位是否与籍贯符合
			// 北京 11 天津12 河北 13 山西14 内蒙古 15
			// 陕西61 甘肃62 青海 63
            String idCardNoHomeCode=idCardNo.substring(0,2);
            //根据身份证号码前两位,在provinceMap中获取正确的籍贯省份名称
            String homeValue=provinceMap.get(idCardNoHomeCode);
            // 获取表格中当前行的籍贯省份
            String home=row.getCell(6).getStringCellValue();
            if(homeValue!=home) {
					errorList. add(String. format("%d行的身份证籍贯
                                            信息不一致! ", rowNum));
			}
            // 4.学历只能填写:大专、本科、硕士、其它
            //通过在eduList列表中查找来判断学历信息是否符合规范
            String eduValue = row.getCell(7). getStringCellValue();
            if(!eduList.contains(eduValue)) {
				errorList. add(String. format("%d行的学历信息
                                            不符合规范! ", rowNum));
			}
		}
    } catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return errorList;

	}

}

运行结果:

1行的身份证籍贯信息不一致!

1行的学历信息不符合规范!

2行的身份证籍贯信息不一致!

3行的身份证籍贯信息不一致!

4行的数据不连续

4行的身份证籍贯信息不一致!

5行的身份证籍贯信息不一致!

5行的学历信息不符合规范!

6行的身份证籍贯信息不一致!

7行的身份证号码长度有误

7行的身份证籍贯信息不一致!

8行的身份证籍贯信息不一致!

9行的身份证号码长度有误

9行的身份证籍贯信息不一致!

9行的学历信息不符合规范!

10行的身份证籍贯信息不一致!

11行的身份证号码长度有误

11行的身份证籍贯信息不一致!

12行的数据不连续

12行的身份证籍贯信息不一致!

13行的身份证号码长度有误

13行的身份证籍贯信息不一致!

14行的身份证籍贯信息不一致!

14行的学历信息不符合规范!

15行的身份证籍贯信息不一致!

15行的学历信息不符合规范!

16行的性别有误

16行的身份证籍贯信息不一致!

17行的身份证籍贯信息不一致!

18行的数据不连续

18行的身份证籍贯信息不一致!

19行的身份证籍贯信息不一致!

20行的身份证籍贯信息不一致!

21行的身份证籍贯信息不一致!

22行的身份证籍贯信息不一致!

23行的身份证籍贯信息不一致!

24行的身份证号码重复

24行的身份证籍贯信息不一致!

25行的性别有误

25行的身份证籍贯信息不一致!

26行的身份证籍贯信息不一致!

27行的身份证籍贯信息不一致!

28行的身份证籍贯信息不一致!

29行的身份证籍贯信息不一致!

30行的身份证籍贯信息不一致!

31行的身份证籍贯信息不一致!

32行的身份证籍贯信息不一致!

33行的身份证号码重复

33行的身份证籍贯信息不一致!

34行的身份证籍贯信息不一致!

35行的身份证籍贯信息不一致!

36行的身份证号码长度有误

36行的身份证籍贯信息不一致!

37行的身份证籍贯信息不一致!

38行的身份证籍贯信息不一致!

39行的身份证籍贯信息不一致!

40行的身份证籍贯信息不一致!

41行的身份证籍贯信息不一致!

42行的身份证籍贯信息不一致!

43行的身份证籍贯信息不一致!

44行的身份证籍贯信息不一致!

45行的身份证籍贯信息不一致!

46行的身份证籍贯信息不一致!

47行的身份证籍贯信息不一致!

相关推荐
m0_5719575838 分钟前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟4 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity5 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天5 小时前
java的threadlocal为何内存泄漏
java
caridle5 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^6 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋36 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx