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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/49786227

背 景


  最近做的一个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时设置单元格类型为数值类型》】

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏谈补锅

开发中常用的JS知识点集锦

802
来自专栏liulun

自己动手写UI库——引入ExtJs(布局)

第一:来看一下最终的效果 ? 第二:来看一下使用方法: ? 第三: Component类代码如下所示: public class Compo...

2035
来自专栏技术小讲堂

ASP.NET AJAX(13)__利用Microsoft AJAX Library开发客户端组件Sys.Component成员Sys.IDisposable成员Sys.INotifyDisposin

Microsoft AJAX Library定义了一个客户端组件的模型,它的基类是Sys.Component,它实现了三个接口Sys.IDisposable,S...

3355
来自专栏进击的君君的前端之路

定时器

1746
来自专栏葡萄城控件技术团队

如何将第三方控件嵌入ToolStrip控件,并提供Design-Time支持

最近研究了一下如何将第三方控件嵌入到ToolStrip控件中,并能提供Design-Time下的支持. 下面将详细讲解如何把系统的MonthCalendar控件...

2078
来自专栏视觉求索无尽也

Markdown:技巧进阶参考资料:开始学习:

本文作者:keloli 本文说明:本文首发于2017.08.01,用于收集Markdown排版中的一些技巧,会不断更新。

1802
来自专栏用户2442861的专栏

在 PyQt4 中的菜单和工具栏¶

http://www.cppblog.com/mirguest/archive/2012/02/05/164982.html

942
来自专栏進无尽的文章

实践-小细节 Ⅰ

     开发中总有一些细枝末节的东西是容易出错的地方,搜集总结下,避免再次掉入坑中。

822
来自专栏腾讯社交用户体验设计

面向未来的 CSS Variable

822
来自专栏向治洪

java的双缓冲技术

Java的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色。在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理。本文从J2SE的一个再现了屏幕闪...

3208

扫码关注云+社区