• 如何正确计算导入Excel的行数(POI/NPOI)


    前言:

    在一些具体的业务实现中经常要使用Excel作为数据的来源,或者导出一些Excel。
    一般我们都是使用POI(Java)/NPOI(.Net)来读取或写入相关的数据。
    这个月接手别人的模块,在模块中需要频繁的导入导出Excel操作,在给那些‘测试’对接时,经常出现导入失败的情况,非常烦人。
    这大概率是POI/NPOI的行数和实际导入的Excel有效行数不一样造成的

    问题再现

    这里以下面的一个Excel为例
    请添加图片描述
    这个Excel没有改格式也没有加空格,它的行数统计如下

    XSSFWorkbook book = new XSSFWorkbook(new FileInputStream("src/Test.xlsx"));
    //结果为8,sheet最后的一行有数据行的行数(从0开始算)		
    System.out.println(book.getSheetAt(0).getLastRowNum());
    //结果为9,sheet实际有数据的总行数	
    System.out.println(book.getSheetAt(0).getPhysicalNumberOfRows());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里随便插入一行再运行便可再次测试
    请添加图片描述

    XSSFWorkbook book = new XSSFWorkbook(new FileInputStream("src/Test.xlsx"));
    //结果为12		
    System.out.println(book.getSheetAt(0).getLastRowNum());
    //结果为10	
    System.out.println(book.getSheetAt(0).getPhysicalNumberOfRows());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.空格造成的行数错误

    导入者不仔细造成的多余空格行通常是这种错误的原因。
    模拟:我在黑框处添加了空格
    请添加图片描述
    这次会得到

    XSSFWorkbook book = new XSSFWorkbook(new FileInputStream("src/Test.xlsx"));
    //15		
    System.out.println(book.getSheetAt(0).getLastRowNum());
    //11		
    System.out.println(book.getSheetAt(0).getPhysicalNumberOfRows());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样,当在程序中使用判断行数的方法时便会出现偏差,导致异常及错误
    像是

    • 获取没有值的单元格的值-------------------------------空指针
    • 获取一些空格的值进入Sql参数------------------------Sql语句错误

    2.格式造成的行数错误

    请注意,有些情况下,导入的Excel的Sheet中存在不同的格式,并不是所有的导入都是默认的通用格式,
    请添加图片描述
    当我选定一片区域改成别的格式的时候,POI判断的长度也会改变
    请添加图片描述

    SSFWorkbook book = new XSSFWorkbook(new FileInputStream("src/Test.xlsx"));
    //18		
    System.out.println(book.getSheetAt(0).getLastRowNum());
    //19		
    System.out.println(book.getSheetAt(0).getPhysicalNumberOfRows());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    有些时候对某些行列改变格式,也会造成POI的行数错误。
    (之前在工作中就有过经历,约定以文本的格式导入Excel,结果不知道什么原因,有人用文本格式拉了几千行,
    DEBUG的时候看到NPOI的获取Sheet的行数上千就非常的懵逼)

    结论:
    故决定不用POI/NPO提供的获取行数的API,必须自己手动计算一下,不然保不齐对接的时候一堆奇奇怪怪的导入出错

    手动计算行数

    POI

    手动计算Sheet的长度要做到一定正确也需要一定的规则
    我们一般
    默认导入Excel的Sheet里的数据不要有中断的空行,即Excel的数据行从首行(一般是列名)开始,一直连续
    请添加图片描述
    所以我们只需要判断单元格的值是否为Null或者是空格(多个空格)且一直连续了多少行,
    下面给出测试代码

    /***
     *  前言:如果你有自己常用的String处理方法,或者框架里的字符串工具,你完全可以不使用下面的流操作判断是否为空字符串
    * 
     * @param sheet          表
     * @param StartNum       数据开始的行号(即除去你的表头占的行数,真正的数据从哪行开始的)
     * @param RequireCol_index  必要数据列的列号(导入Excel中任意一个不为空列的列号即可)
     * @return Excel的sheet的有效行数
     */
    public static int GetRealRowNum(Sheet sheet,int StartNum,int RequireCol_index) {
    		int Count = 0;
    
    		   
    		    //这里第一个条件可以判断单元格非空,第二个则利用流操作去判断是否其中所有的字符全是空字符或空格字符。 
    			while(sheet.getRow(StartNum).getCell(RequireCol_index)!=null&&
    					!ToList(sheet.getRow(StartNum).getCell(RequireCol_index).toString().toCharArray())
    					.stream().allMatch((Character item)->{
    						if(item.equals(' ')||item.toString().equals(""))
    							return true;
    						else
    							return false;
    					})
    					)
    			{
    				//用Count计算有效长度
    				StartNum++;Count++;
    			}
    		}catch (Exception e) {
    			
    		}
    		return Count;
    	}
    	
    	public  static List<Character> ToList(char[] charArray){
    		List<Character> chars = new ArrayList<Character>(); 
    		
    		for(int i=0;i<charArray.length;i++)
    			chars.add(charArray[i]);
    		
    		return chars;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    测试一下,
    还是上面那张被修改了格式的Excel
    请添加图片描述
    在里面随机埋了写空格,并选中一块敌法更改格式,得到的结果如下

    public static void main(String[] args) throws IOException {
    		
    		
    	XSSFWorkbook book = new XSSFWorkbook(new FileInputStream("src/Test.xlsx"));
    	//28
    	System.out.println(book.getSheetAt(0).getLastRowNum());
    	//24
    	System.out.println(book.getSheetAt(0).getPhysicalNumberOfRows());
    	//8
    	System.out.println(GetRealRowNum(book.getSheetAt(0), 1, 0));
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    大多数情况下是可以用的,但实际的工作中肯定是使用字符串工具来判断是否为空格字符串或空字符串

    //直接用字符串工具久非常的简单明了
    while(sheet.getRow(StartNum).getCell(RequireCol_index)!=null&&
    	!StringUtil.isBlank(sheet.getRow(StartNum).getCell(RequireCol_index).toString()))
    {
    	StartNum++;Count++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果也是上面一样。
    注:(StringUtils/StringUtils有非常多的同名类,且方法也会各有不同。例子用到的是org.jsoup.internal.StringUtil)

    NPOI

    NPOI的使用方法也类似,

    int start = 1, 
    count_row = 0;
    try
    {
        
        while (sheet.GetRow(start).GetCell(0) != null && !string.IsNullOrWhiteSpace((sheet.GetRow(start).GetCell(0).ToString()))
       {
          count_row++;start++;
       }
    }catch(Exception e)
    {
    
    }
    return count_row ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    Java基础教程:dubbo源码解析-SPI机制
    二维码智慧门牌管理系统:打造智慧城市之路
    需求工程咨询和实施服务
    Express基础
    Gateway服务网关
    AI 绘画 | Stable Diffusion 图生图
    简单神经网络算法原理,最简单的神经网络算法
    【python】无限量PPT免费下载?找模板在不怕心仪得不能用啦
    nrf52832 PWM配置
    如何使用 CSS 定位 HTML 元素
  • 原文地址:https://blog.csdn.net/jamenu/article/details/125394822