【POI框架实战】——POI导出Excel时设置单元格类型为数值类型




背 景


  最近做的一个ITFIN的项目中,后台需要用POI实现导出功能,导出的数据中有文本格式,也有货币格式,所以为了方便在将来导出的表格中做计算,存放货币的单元格需要设置为数值类型。

  导出的Excel的单元格都是文本格式(单元格左上角有个小三角):

这里写图片描述

  费了不少功夫,终于把“小三角”去掉了,这里总结并分享一下问题的解决方法。


  通过poi导出excel的过程大致是这样的:

     规定单元格的格式
        ↓
      创建单元格
        ↓
     设置单元格的格式
        ↓
     设置数据的格式
        ↓
    把数据存放到单元格中
        ↓
      通过IO流输出


背景POI导出Excel时设置单元格类型为数值类型

  要想存放数值的单元格以数值类型导出,其中最关键的步骤就是上面加粗的两步,设置单元格的格式和向单元格中存放数据。

  核心代码如下:

	/**
	 * 导出Excel-2015年11月11日
	 * 
	 *@param outPutParam Excel数据实体,包括要导出的excel标头、列标题、数据等
	 * */
	private void createContentRows(ExcelParam outPutParam) {
		HSSFWorkbook workbook=new HSSFWorkbook(); //创建一个Excel文件
		// 遍历集合数据,产生数据行
		for (int i = 0; i < outPutParam.getContent().size(); i++) {
			int rowIndex = i + 2;
			HSSFRow contentRow = sheet.createRow(rowIndex);
			Map<String, Object> rowDate = outPutParam.getContent().get(i);
			//遍历列
			for (int j = 0; j < outPutParam.getTitleList().size(); j++) {		
				Title headTitle = outPutParam.getTitleList().get(j);//获取第i行第j列列标题
				String headerName = headTitle.getName();//获取第j列列标识
				Object data = rowDate.get(headerName);//获取第i行第j列所放数据
				HSSFCellStyle contextstyle =workbook.createCellStyle();
				HSSFCell contentCell = contentRow.createCell(j);				
				Boolean isNum = false;//data是否为数值型
				Boolean isInteger=false;//data是否为整数
				Boolean isPercent=false;//data是否为百分数
				if (data != null || "".equals(data)) {
					//判断data是否为数值型
					isNum = data.toString().matches("^(-?\\d+)(\\.\\d+)?$");
					//判断data是否为整数(小数部分是否为0)
					isInteger=data.toString().matches("^[-\\+]?[\\d]*$");
					//判断data是否为百分数(是否包含“%”)
					isPercent=data.toString().contains("%");
				}
				
				//如果单元格内容是数值类型,涉及到金钱(金额、本、利),则设置cell的类型为数值型,设置data的类型为数值类型
				if (isNum && !isPercent) {
					HSSFDataFormat df = workbook.createDataFormat(); // 此处设置数据格式
					if (isInteger) {
						contextstyle.setDataFormat(df.getBuiltinFormat("#,#0"));//数据格式只显示整数
					}else{
						contextstyle.setDataFormat(df.getBuiltinFormat("#,##0.00"));//保留两位小数点
					}					
					// 设置单元格格式
					contentCell.setCellStyle(contextstyle);
					// 设置单元格内容为double类型
					contentCell.setCellValue(Double.parseDouble(data.toString()));
				} else {
					contentCell.setCellStyle(contextstyle);
					// 设置单元格内容为字符型
					contentCell.setCellValue(data.toString());
				}
			}
		}
	}

  如上,有两个比较重要的点:
  1、先用正则表达式判断数据是否为数值型,如果为数值型,则设置单元格格式为整数或者小数;
  2、然后往单元格中存放数据的时候要设置数据的格式为double类型,如果查看poi的源码HSSFCell.java会发现设置数据的方法如下,所以用setCellValue(double)方法即可。

这里写图片描述


优化

  到了这里,您可能以为万事大吉啊了,其实上面的代码有个陷阱,如果不经过大数据量的测试是发觉不出来的哦~~

  如果数据量大的话,系统可能会报错“The maximum number of cell styles was exceeded. You can define up to 4000 styles in a .xls workbook”,原因是style创建的次数太多了,解决这个问题的方法很简单,在循环体外面创建单元格格式contextstyle(即把它当成一个“全局”变量),不要在循环内部创建。

  正确的代码如下:

	/**
	 * 导出Excel-2015年11月11日
	 * 
	 *@param outPutParam Excel数据实体,包括要导出的excel标头、列标题、数据等
	 * */
	private void createContentRows(ExcelParam outPutParam) {
		HSSFWorkbook workbook=new HSSFWorkbook(); //创建一个Excel文件
		HSSFCellStyle contextstyle =workbook.createCellStyle();
		// 遍历集合数据,产生数据行
		for (int i = 0; i < outPutParam.getContent().size(); i++) {
			int rowIndex = i + 2;
			HSSFRow contentRow = sheet.createRow(rowIndex);
			Map<String, Object> rowDate = outPutParam.getContent().get(i);
			//遍历列
			for (int j = 0; j < outPutParam.getTitleList().size(); j++) {		
				Title headTitle = outPutParam.getTitleList().get(j);//获取第i行第j列列标题
				String headerName = headTitle.getName();//获取第j列列标识
				Object data = rowDate.get(headerName);//获取第i行第j列所放数据
				HSSFCell contentCell = contentRow.createCell(j);				
				Boolean isNum = false;//data是否为数值型
				Boolean isInteger=false;//data是否为整数
				Boolean isPercent=false;//data是否为百分数
				if (data != null || "".equals(data)) {
					//判断data是否为数值型
					isNum = data.toString().matches("^(-?\\d+)(\\.\\d+)?$");
					//判断data是否为整数(小数部分是否为0)
					isInteger=data.toString().matches("^[-\\+]?[\\d]*$");
					//判断data是否为百分数(是否包含“%”)
					isPercent=data.toString().contains("%");
				}
				
				//如果单元格内容是数值类型,涉及到金钱(金额、本、利),则设置cell的类型为数值型,设置data的类型为数值类型
				if (isNum && !isPercent) {
					HSSFDataFormat df = workbook.createDataFormat(); // 此处设置数据格式
					if (isInteger) {
						contextstyle.setDataFormat(df.getBuiltinFormat("#,#0"));//数据格式只显示整数
					}else{
						contextstyle.setDataFormat(df.getBuiltinFormat("#,##0.00"));//保留两位小数点
					}					
					// 设置单元格格式
					contentCell.setCellStyle(contextstyle);
					// 设置单元格内容为double类型
					contentCell.setCellValue(Double.parseDouble(data.toString()));
				} else {
					contentCell.setCellStyle(contextstyle);
					// 设置单元格内容为字符型
					contentCell.setCellValue(data.toString());
				}
			}
		}
	}

  最后导出的正确格式:

这里写图片描述




【 转载请注明出处——胡玉洋《POI导出Excel时设置单元格类型为数值类型》


  • 9
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 48
    评论
Apache poi 导出excel合并单元格可以使用setRegionStyle方法来设置合并单元格的样式。这个方法接受三个参数,分别是Sheet对象、CellRangeAddress对象和CellStyle对象。在这个方法中,通过循环遍历合并单元格的每一行和每一列,然后获取对应的单元格设置样式。具体的代码实现可以参考引用\[1\]中的示例代码。 此外,还可以使用PoiModel类来定义导出excel的数据模。这个类包含了内容、上一行同一位置内容、行标、列标等属性。可以根据实际需求来使用这个类。具体的代码实现可以参考引用\[2\]和引用\[3\]中的示例代码。 总结起来,Apache poi 导出excel合并单元格的步骤包括设置合并单元格的样式和定义导出数据的模。可以根据具体的需求来使用相应的方法和类来实现导出功能。 #### 引用[.reference_title] - *1* *3* [poi 导出Excel 动态 合并单元格](https://blog.csdn.net/weixin_65436454/article/details/127806178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [使用POI 导出Excel 动态合并单元格](https://blog.csdn.net/weixin_41722928/article/details/112849624)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值