JAVA 讀取excel(支持xlsx、xls格式, 支持合併單元格)

記錄一下最近使用java操作excel的代碼。

  1. 導包。注意兩者版本需要一致
<dependency>
  <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.9</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
</dependency>
  1. 解析excel代碼
public class ExcelDemo {

	 public static void main(String[] args) {
         // 獲取輸入流
          InputStream in = getResourcesFileInputStream("Test.xlsx");
          //InputStream in = getResourcesFileInputStream("Test.xls");

          // 解析---1
          // 解析Excel所有sheet,所有行,所有列
          List<List<String[]>> allData = ExcelUtil.read(in);

          // 解析---2
          // 解析第一個sheet數據
          ExcelUtil.readSheet(in, 0, sheet -> {
              //解析第3行到最後一行,第2列到第9列的數據
              List<String[]> source = ExcelUtil.readSheetPart(sheet, 2, sheet.getLastRowNum(), 1, 8);
          });

          // 解析---3
          // 完全自定義解析
          ExcelUtil.readByCustomize(in, wb -> {
              // doSomeThing()...可參照ExcelUtil.readExcel()方法
          });

		  in.close();
      }

       /**
        * 加載Resources目錄下的文件
        * @param fileName 文件名
        * @return
        */
     public static InputStream getResourcesFileInputStream(String fileName){
          // Resources目錄的絕對路徑
          // String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
          InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
          return in;
	 }
}

注意事項獲取的excel行數不正確解決方法

  1. Excel工具類
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Excel工具類
 */
public class ExcelUtil {

    private static Logger logger = LoggerFactory.getLogger(ExcelUtil.class);

    /**
     * 對excel單個sheet進行處理的接口
     */
    public interface SheetHandler {
        /**
         * 對sheet進行處理
         * @param sheet
         */
        void handle(Sheet sheet);
    }

    /**
     * 對excel進行處理的接口
     */
    public interface WorkBookHandler {
        /**
         * 對excel進行處理
         * @param wb
         * @return
         */
        void handle(Workbook wb);
    }

    /**
     * 讀取excel指定sheet數據<br>未關閉io
     * @param  in
     */
    public static void readSheet(InputStream in, int sheetIdx, SheetHandler handler) throws IOException, InvalidFormatException {
        // 創建工作簿
        Workbook wb = createWorkBook(in);
        // 接口回調處理
        handler.handle(getSheetByIdx(wb, sheetIdx));
    }


    /**
     * 讀取excel全部sheet全部行數據<br>未關閉io
     * @param  in
     * @return List<List<String[]>> <br>List<String[]>表示第i個sheet<br> String[]表示sheet某行
     */
    public static List<List<String[]>> read(InputStream in) throws IOException, InvalidFormatException {
        // 創建工作簿
        Workbook wb = createWorkBook(in);
        // 默認讀取所有行,所有列
        return readExcel(wb);
    }

    /**
     * 讀取excel<br>自定義解析方式<br>未關閉io
     * @param  in
     * @return
     */
    public static void readByCustomize(InputStream in, WorkBookHandler handler) throws IOException, InvalidFormatException {
        // 創建工作簿
        Workbook wb = createWorkBook(in);

        handler.handle(wb);
    }

    /**
     * 讀取指定sheet的範圍行,範圍列
     * @param sheet excel 某個sheet
     * @param startRow 開始行下標
     * @param endRow 結束行下標
     * @param startCol 開始列下標
     * @param endCol 結束列下標
     * @return 指定範圍的數據。一個String[]是一行數據
     */
    public static List<String[]> readSheetPart(Sheet sheet, int startRow, int endRow, int startCol, int endCol){

        if(sheet == null){
            logger.warn("excel sheet is not exist");
            return null;
        }

        int firstRow = sheet.getFirstRowNum();
        int lastRow = sheet.getLastRowNum();
        logger.info("sheet firstRow:"+firstRow);
        logger.info("sheet lastRow:"+lastRow);
        if(startRow < 0 || startRow > lastRow || startRow > endRow){
            return null;
        }
        if(endRow > lastRow){
            return null;
        }

        List<String[]> list = new ArrayList<>();
        // 逐行解析
        for (int i = startRow; i <= endRow ; i++) {
            Row row = sheet.getRow(i);
            // 過濾空行
            if(row == null){
                continue;
            }
            int firstCol = row.getFirstCellNum();
            int lastCol = row.getLastCellNum();
            logger.info("row("+i+") firstCol:"+firstCol);
            logger.info("row("+i+") lastCol:"+lastCol);
            if(startCol < 0 || startCol > lastCol || startCol > endCol){
                return null;
            }
            if(endCol > lastCol){
                return null;
            }

            int colIdx = 0;
            int objIdx = 0;
            String[] objs = new String[endCol-startCol+1];
            // 逐列解析
            for (Cell c : row) {
                // 讀取指定列數據
                if(colIdx >= startCol && colIdx <= endCol){
                    c.setCellType(Cell.CELL_TYPE_STRING);
                    boolean isMerge = isMergedRegion(sheet, i, c.getColumnIndex());
                    String data = "";
                    //判斷是否具有合併單元格
                    if(isMerge) {
                        data = getMergedRegionValue(sheet, row.getRowNum(), c.getColumnIndex());
                    }else {
                        data = c.getRichStringCellValue().toString();
                    }
                    objs[objIdx++]=data;
                }
                colIdx++;
            }
            list.add(objs);
        }

        if(list.size() == 0){
            logger.warn("excel sheet data is space");
        }

        return list;
    }

    /**
     * 讀取excel文件
     * @param  wb
     * @return List<List<String[]>> <br>List<String[]>表示第i個sheet<br> String[]表示sheet某行
     */
    public static List<List<String[]>> readExcel(Workbook wb) {

        // 總sheet數
        int sheetNum = wb.getNumberOfSheets();
        logger.info("excel sheetNum is :"+sheetNum);

        // 遍歷sheet
        List<List<String[]>> list = new ArrayList<>();
        for (int i = 0; i < sheetNum ; i++) {
            Sheet sheet = wb.getSheetAt(i);
            List<String[]> sheetData = readOneSheet(sheet);
            list.addAll(Collections.singleton(sheetData));
        }

        return list;
    }

    /**
     * 讀取單個sheet數據,封裝爲List<String[]><br>
     * @warn 可能會有全是空串的數據
     * @return List<String[]>,一個String[]是
     */
    public static List<String[]> readOneSheet(Sheet sheet){
        int startRow = sheet.getFirstRowNum();
        int endRow = sheet.getLastRowNum();
        logger.info("sheet startRow:"+startRow);
        logger.info("sheet endRow:"+endRow);
        List<String[]> list = new ArrayList<>();
        // 遍歷行
        for (int i = startRow; i <= endRow ; i++) {
            Row row = sheet.getRow(i);
            if(row == null){
                continue;
            }
            // int startColIdx = row.getFirstCellNum();
            int endColIdx = row.getLastCellNum();
            String[] objs = new String[endColIdx+1];
            int colIdx = 0;
            // 遍歷列
            for (Cell c : row) {
                c.setCellType(Cell.CELL_TYPE_STRING);
                String data = "";
                boolean isMerge = isMergedRegion(sheet, i, c.getColumnIndex());
                //判斷是否具有合併單元格
                if(isMerge) {
                    data = getMergedRegionValue(sheet, row.getRowNum(), c.getColumnIndex());
                }else {
                    data = c.getRichStringCellValue().toString();
                }
                objs[colIdx]=data;
                colIdx++;
            }
            list.add(objs);
        }

        if(list.size() == 0){
            logger.warn("excel sheet data is space");
        }

        return list;
    }

    /**
     * 獲取合併單元格的值<br>
     * 即獲取合併單元格第一個cell的值
     * @param sheet
     * @param row
     * @param column
     * @return
     */
    public static String getMergedRegionValue(Sheet sheet ,int row , int column){

        // 獲得一個 sheet 中合併單元格的數量
        int sheetMergeCount = sheet.getNumMergedRegions();

        // 遍歷合併單元格
        for(int i = 0 ; i < sheetMergeCount ; i++){

            // 得出具體的合併單元格
            CellRangeAddress ca = sheet.getMergedRegion(i);

            // 得到合併單元格的起始行, 結束行, 起始列, 結束列
            int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();

            // 獲取合併單元格第一個cell的值
            if(row >= firstRow && row <= lastRow){
                if(column >= firstColumn && column <= lastColumn){
                    Row fRow = sheet.getRow(firstRow);
                    Cell fCell = fRow.getCell(firstColumn);
                    return getCellValue(fCell) ;
                }
            }
        }

        return null ;
    }

    /**
     * 判斷指定的單元格是否是合併單元格
     * @param sheet
     * @param row 行下標
     * @param column 列下標
     * @return
     */
    public static boolean isMergedRegion(Sheet sheet,int row ,int column) {

        // 得到一個sheet中有多少個合併單元格
        int sheetMergeCount = sheet.getNumMergedRegions();
        for (int i = 0; i < sheetMergeCount; i++) {

            // 得出具體的合併單元格
            CellRangeAddress range = sheet.getMergedRegion(i);

            // 得到合併單元格的起始行, 結束行, 起始列, 結束列
            int firstColumn = range.getFirstColumn();
            int lastColumn = range.getLastColumn();
            int firstRow = range.getFirstRow();
            int lastRow = range.getLastRow();

            // 判斷該單元格是否在合併單元格範圍之內, 如果是, 則返回 true
            if(row >= firstRow && row <= lastRow){
                if(column >= firstColumn && column <= lastColumn){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判斷sheet頁中是否含有合併單元格
     * @param sheet
     * @return
     */
    public static boolean hasMerged(Sheet sheet) {
        return sheet.getNumMergedRegions() > 0 ? true : false;
    }

    /**
     * 獲取單元格的值
     * @param cell
     * @return
     */
    public static String getCellValue(Cell cell){
        if(cell == null) return "";
        switch (cell.getCellType()){
            case Cell.CELL_TYPE_STRING:
                return cell.getStringCellValue();
            case Cell.CELL_TYPE_BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case Cell.CELL_TYPE_FORMULA:
                return cell.getCellFormula() ;
            case Cell.CELL_TYPE_NUMERIC:
                return String.valueOf(cell.getNumericCellValue());
            default:
                return "";
        }
    }

    /**
     * 創建工作簿
     * @param in
     */
    private static Workbook createWorkBook(InputStream in) throws IOException, InvalidFormatException {
        Workbook wb = WorkbookFactory.create(in);
        return wb;
    }

    /**
     * 獲取sheet
     * @param wb
     * @param sheetIdx
     */
    public static Sheet getSheetByIdx(Workbook wb, int sheetIdx){

        // 總sheet數
        int sheetNum = wb.getNumberOfSheets();

        // sheet不存在
        if(sheetIdx < 0 || sheetIdx >= sheetNum){
            return null;
        }

        // 獲取sheet
        Sheet sheet = wb.getSheetAt(sheetIdx);

        return sheet;
    }


    /*----------寫excel相關----------*/
    /**
     * 添加合併單元格
     * @param sheet
     * @param firstRow 開始行
     * @param lastRow 結束行
     * @param firstCol 開始列
     * @param lastCol 結束列
     */
    public static void mergeRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
        sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章