zoukankan      html  css  js  c++  java
  • 导入Excel——解析Excel

    读取Excel

    思路:先读取整个Excel,即工作簿,再依次读取其中的每个工作表Sheet,最后读取工作表内的表格。

    一、读取工作簿
    利用流读取指定目录中的工作簿,并写入内存。

       /**
         * Constructs a XSSFWorkbook object, by buffering the whole stream into memory
         *  and then opening an {@link OPCPackage} object for it.
         * 
         * <p>Using an {@link InputStream} requires more memory than using a File, so
         *  if a {@link File} is available then you should instead do something like
         *   <pre><code>
         *       OPCPackage pkg = OPCPackage.open(path);
         *       XSSFWorkbook wb = new XSSFWorkbook(pkg);
         *       // work with the wb object
         *       ......
         *       pkg.close(); // gracefully closes the underlying zip file
         *   </code></pre>
         */
        public XSSFWorkbook(InputStream is) throws IOException {
            super(PackageHelper.open(is));
    
            beforeDocumentRead();
            
            // Build a tree of POIXMLDocumentParts, this workbook being the root
            load(XSSFFactory.getInstance());
    
            // some broken Workbooks miss this...
            if(!workbook.isSetBookViews()) {
                CTBookViews bvs = workbook.addNewBookViews();
                CTBookView bv = bvs.addNewWorkbookView();
                bv.setActiveTab(0);
            }
        }

    针对源码,先生产InputStream,再读取:

    InputStream is = new FileInputStream(filePath);
    XSSFWorkbook workbook = new XSSFWorkbook(is);

    二、读取工作薄中工作表,依次读取,先要知道到底有几个? 此处,需要对XSSFWorkbook类有基本的了解。(建议看一下源码)

    int countSheet = workbook.getNumberOfSheets();

    for循环读取每个工作表。
    思考:需要怎样的数据结构才能优雅的存放其中的数据?
    思考一下工作表中存放的数据:主流的是表格和表格下的数据。
    进一步思考这个表格的数据用什么样的数据结构?
    表格进一步分析,表格有表头和非表头数据组成,由于表头需要对应实体映射,需要单独处理。
    因此表头采用Map存储(此处下面需要继续分析),同样一行数据也用Map处理,多行数据放入List
    至此:工作表的数据结构清晰了。

    表头:Map<String,object>;
    非数据多行:List<Map,Object>;

    工作表名称:String;

    /**
     * Excel解析后相关的Excel
     * <一句话功能简述>
     * <功能详细描述>
     * 
     * @author
     * @version  [版本号, 2019年6月26日]
     * @see  [相关类/方法]
     * @since  [产品/模块版本]
     */
    public class ExcelSheetStruct {
        
        /**
         * sheet页名称
         */
        private String sheetName;
        
        /**
         * 列表表头
         */
        private Map<String,String> headerMap;
        
        /**
         * 列表数据
         */
        private List<Map<String,Object>> data;
        // 省略get和set
    }

    三、工作表的数据读取

    for (int numSheet = 0; numSheet < countSheet; numSheet++) {
        // 获取Sheet,创建Sheet数据结构
         XSSFSheet xssfSheet = workbook.getSheetAt(numSheet);
         ExcelSheetStruct excelSheetStruct = new ExcelSheetStruct();
         excelSheetStruct.setSheetName(xssfSheet.getSheetName());      
    }

    再获取表格数据,先得获取有效区域。
    思考:到底是先从行开始,还是先从列开始,即第一维度是行还是列?
    此处先从行开始,因为行在Sheet中是数字。(注:有效行是当前行数字减1)

    //处理有数据区域的表格
    int firstRowNum = xssfSheet.getFirstRowNum();
    int lastRowNum = xssfSheet.getLastRowNum();
    // 处理表头和非表头,第一维度“行”
    for (int row = firstRowNum; row <= lastRowNum; row++) {}

    再for循环内,获取列号(第二维度),就能唯一定位到数据,进行获取。
    这边先获取表头

        /**
         * 根据所给的首行,生成具体的数据结构
         * <一句话功能简述>
         * <功能详细描述>
         * @param xssfRow
         * @return
         * @see [类、类#方法、类#成员]
         * @author
         */
        public static Map<String, String> generateHeadLine(XSSFRow xssfRow) {
            Map<String, String> headerMap = new HashMap<>();
            // 获取开始列和结束列
            int firstCellNum = xssfRow.getFirstCellNum();
            int lastCellNum = xssfRow.getLastCellNum();
            for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
                Cell cell = xssfRow.getCell(cellNum, XSSFRow.CREATE_NULL_AS_BLANK);
                // key——列号,value——数据
                headerMap.put(String.valueOf(cellNum), cell.getStringCellValue());
            }
            return headerMap;
        }

    同理,获取非表头。此处把表头和下面列的数据进行关联。即Map<String,Object> = HashMap<"表头","表头当前列下面的一个数据(当前cell的值)">

        /**
         * 对数据行的处理,根据头行数据,将数据与行列表结合起来
         * <一句话功能简述>
         * <功能详细描述>
         * @param xssfRow
         * @return
         * @see [类、类#方法、类#成员]
         * @author
         */
        public static Map<String, Object> generateDataLine(XSSFRow xssfRow, Map<String, String> headerLine) {
            Map<String, Object> mapDate = new HashMap<>();
            // 此处也是读取列,用上面有不同之处,这边不是顺序读取,是按照表头存储的数据的key(列号)
            for (String key : headerLine.keySet()) {
                Cell cell = xssfRow.getCell(Integer.parseInt(key), XSSFRow.CREATE_NULL_AS_BLANK);
                cell.setCellType(Cell.CELL_TYPE_STRING);
                // HashMap<"表头的值","当前的值">
                mapDate.put(headerLine.get(key), cell.getStringCellValue());
            }
            return mapDate;
        }

    读取完一行非表头数据需要继续读取,最后添加到非标头数据结构中List<Map>;

    //循环处理该sheet页中的单行数据
        for (int row = firstRowNum; row <= lastRowNum; row++) {
            XSSFRow xssfRow = xssfSheet.getRow(row);
            if (row == firstRowNum) {
                // 处理完表头数据,把它添加到Sheet中
                excelSheetStruct.setHeaderMap(generateHeadLine(xssfRow));
                continue;
            }
            Map<String, Object> dateLine = generateDataLine(xssfRow, excelSheetStruct.getHeaderMap());
            // 整合非表头数据
            dataList.add(dateLine);
        }
    // 处理完非表头数据,把它添加到Sheet中
    allDataList.add(excelSheetStruct);

    至此数据单个Sheet处理完毕,回顾代码:

    /**
         * 读取Excel生成相应的数据集
         * <一句话功能简述>
         * <功能详细描述>
         * @param filePath
         * @return
         * @throws IOException
         * @see [类、类#方法、类#成员]
         * @author
         */
        public static List<ExcelSheetStruct> readXlsx(String filePath)
            throws IOException {
            
            List<ExcelSheetStruct> allDataList = new ArrayList<>();
            XSSFWorkbook workbook = null;
            InputStream is = new FileInputStream(filePath);
            workbook = new XSSFWorkbook(is);
            int countSheet = workbook.getNumberOfSheets();
            
            for (int numSheet = 0; numSheet < countSheet; numSheet++) {
                ExcelSheetStruct excelSheetStruct = new ExcelSheetStruct();
                List<Map<String, Object>> dataList = new ArrayList<>();
                excelSheetStruct.setData(dataList);
                
                XSSFSheet xssfSheet = workbook.getSheetAt(numSheet);
                excelSheetStruct.setSheetName(xssfSheet.getSheetName());
                
                //处理有数据区域的表格
                int firstRowNum = xssfSheet.getFirstRowNum();
                int lastRowNum = xssfSheet.getLastRowNum();
                
                if (lastRowNum == 0) {
                    continue;
                }
                
                //循环处理该sheet页中的单行数据
                for (int row = firstRowNum; row <= lastRowNum; row++) {
                    XSSFRow xssfRow = xssfSheet.getRow(row);
                    if (row == firstRowNum) {
                        excelSheetStruct.setHeaderMap(generateHeadLine(xssfRow));
                        continue;
                    }
                    Map<String, Object> dateLine = generateDataLine(xssfRow, excelSheetStruct.getHeaderMap());
                    dataList.add(dateLine);
                }
                allDataList.add(excelSheetStruct);
            }
            workbook.close();
            is.close();
            return allDataList;
            
        }
  • 相关阅读:
    SpringBoot笔记
    SpringBoot面试篇
    多线程篇
    Tomcat篇
    Redis篇
    Nginx篇
    JVM篇
    MySQL篇
    python ETL工具 pyetl
    python通用数据库操作工具 pydbclib
  • 原文地址:https://www.cnblogs.com/gzhcsu/p/11120573.html
Copyright © 2011-2022 走看看