java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java解析Excel

Java实现解析Excel复杂表头

作者:✘迟暮

这篇文章主要为大家详细介绍了如何使用Java实现解析Excel复杂表头功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

在Java中如果想解析复杂的Excel表头,可以使用Apache POI库。

废话不多说,直接上源码。前后端都有哦

后端

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
/**
 * @Description:Excel工具类(复杂表头解析)
 * @Author: sh
 * @Date: 2025/1/2 09:29
 */
public class ExcelComplexUtil {
    /**
     * 导入Excel文件,逐行读取数据,数据格式二维数组
     * @param filePath
     * @param sheetIndex
     * @return
     * @throws IOException
     */
    public String[][] importExcel(String filePath, int sheetIndex) throws IOException {
        List<String[]> dataList = new ArrayList<>();
 
        try (FileInputStream fis = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fis)) {
 
            Sheet sheet = workbook.getSheetAt(sheetIndex); // 获取第一个工作表
            // 获取表头行
            Row headerRow = checkHeaderRow(sheet);
 
            if (headerRow != null) {
                //封装表头数据
                warpHeaderData(headerRow, dataList);
            } else {
                throw new RuntimeException("Excel 文件中没有找到表头行,请修改表格");
            }
 
            // 读取数据行
            for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
 
                if (row == null) {
                    continue; // 跳过空行
                }
                //封装行数据
                warpRowData(headerRow, row, dataList);
            }
        }
 
        // 将 List<String[]> 转换为 String[][] 数组
        String[][] result = new String[dataList.size()][];
        for (int i = 0; i < dataList.size(); i++) {
            result[i] = dataList.get(i);
        }
 
        return result; // 返回二维数组
    }
 
    /**
     * 检查表头行,表头行必须在前10行中
     * @param sheet
     * @return
     */
    private Row checkHeaderRow(Sheet sheet) {
        int i = 0;
        Row headerRow = null;
 
        while (i < 10) {
            headerRow = sheet.getRow(i);
            if (headerRow != null) {
                break;
            }
 
            i++;
        }
 
        return headerRow;
    }
 
    /**
     * 数据遍历
     * @param headerRow
     * @param dataList
     * @throws IOException
     */
    public void warpHeaderData(Row headerRow, List<String[]> dataList) {
        int columnCount = headerRow.getPhysicalNumberOfCells();
        short lastCellNum = headerRow.getLastCellNum();
 
        String[] data = new String[columnCount]; // 创建一维数组存储表头数据
        for (int colIndex = 0; colIndex < columnCount; colIndex++) {
            Cell cell = headerRow.getCell(lastCellNum - columnCount + colIndex);
            String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格
            data[colIndex] = cellValue; // 将单元格值放入表头数组中
        }
 
        dataList.add(data); // 将表头数组添加到列表中
    }
 
    public void warpRowData(Row headerRow, Row row, List<String[]> dataList) {
        int columnCount = headerRow.getPhysicalNumberOfCells();
        short lastCellNum = headerRow.getLastCellNum();
 
        String[] data = new String[columnCount]; // 创建一维数组存储表头数据
        for (int colIndex = 0; colIndex < columnCount; colIndex++) {
            Cell cell = row.getCell(lastCellNum - columnCount + colIndex);
            String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格
            data[colIndex] = cellValue; // 将单元格值放入表头数组中
        }
 
        dataList.add(data); // 将表头数组添加到列表中
    }
 
    /**
     * 获取excel中所有合并单元格
     * @param filePath
     * @param sheetIndex
     * @throws IOException
     */
    public List<MergedCell> checkMergedCells(String filePath, int sheetIndex) throws IOException {
        try (FileInputStream fis = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fis)) {
 
            Sheet sheet = workbook.getSheetAt(sheetIndex); // 获取第一个工作表
            int numberOfMergedRegions = sheet.getNumMergedRegions(); // 获取合并单元格的数量
 
            List<MergedCell> mergedCellArray = new ArrayList<>();
 
            for (int i = 0; i < numberOfMergedRegions; i++) {
                MergedCell mergedCell = new MergedCell();
 
                CellRangeAddress range = sheet.getMergedRegion(i); // 获取合并单元格区域
                mergedCell.setRange(range.formatAsString());
                // 获取合并单元格区域的起始单元格
                int firstRow = range.getFirstRow();
                int firstCol = range.getFirstColumn();
 
                // 获取合并单元格的内容
                Row row = sheet.getRow(firstRow);
                Cell cell = row.getCell(firstCol);
                String cellValue = (cell != null) ? cell.toString() : ""; // 处理空单元格
                mergedCell.setValue(cellValue);
 
                mergedCellArray.add(mergedCell);
            }
 
            return mergedCellArray;
        }
    }
 
    /**
     * 检查特定单元格是否是合并单元格
     * @param sheet
     * @param row
     * @param col
     * @return
     */
    private boolean isMergedCell(Sheet sheet, int row, int col) {
        int numberOfMergedRegions = sheet.getNumMergedRegions();
        for (int i = 0; i < numberOfMergedRegions; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            if (range.isInRange(row, col)) {
                return true; // 如果该单元格在合并区内,返回 true
            }
        }
        return false; // 如果不在任何合并区内,返回 false
    }
 
    class MergedCell {
        private String range;
        private String value;
 
        public String getRange() {
            return range;
        }
 
        public void setRange(String range) {
            this.range = range;
        }
 
        public String getValue() {
            return value;
        }
 
        public void setValue(String value) {
            this.value = value;
        }
 
        @Override
        public String toString() {
            return "MergedCell{" +
                    "range='" + range + '\'' +
                    ", value='" + value + '\'' +
                    '}';
        }
    }
 
    //    public static void main(String[] args) {
//        String filePath = "/ceshi/ceshi.xlsx"; // Excel 文件路径
//        try {
//            String[][] strings = importExcel(filePath, 0);
//            for (String[] row : strings) {
//                System.out.println(String.join(", ", row)); // 以逗号为分隔符打印每一行
//            }
//            List<MergedCell> mergedCells = checkMergedCells(filePath, 0);
//            for (MergedCell row : mergedCells) {
//                System.out.println(row); // 以逗号为分隔符打印每一行
//            }
//
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
}

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>合并单元格查看器</title>
    <style>
        table {
            border-collapse: collapse;
            width: 500px; /* 设置表格宽度 */
        }
        td {
            border: 1px solid black;
            padding: 0; /* 清除内边距 */
            text-align: center;
            vertical-align: middle; /* 垂直居中 */
            height: 50px; /* 设置每个单元格的高度 */
        }
        .merged {
            background-color: #f0f0f0; /* 合并单元格的背景颜色 */
            font-weight: bold; /* 合并单元格字体加粗 */
        }
    </style>
</head>
<body>
    <div id="app">
        <h1>合并单元格查看器</h1>
        <table>
            <tr v-for="(row, rowIndex) in tableData" :key="rowIndex">
                <td
                    v-for="(cell, colIndex) in row"
                    :key="colIndex"
                    v-if="!isCellOccupied(rowIndex, colIndex)"
                    :rowspan="getRowSpan(rowIndex, colIndex)"
                    :colspan="getColSpan(rowIndex, colIndex)"
                    :class="{ merged: isMergedCell(rowIndex, colIndex) }"
                >
                    {{ getCellValue(rowIndex, colIndex) }}
                </td>
            </tr>
        </table>
    </div>
 
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    mergedCells: [
                        { range: "C1:D1", value: "合并单元格 1" },
                        { range: "B2:C2", value: "合并单元格 2" },
                        { range: "B3:C3", value: "合并单元格 " },
                        { range: "A2:A3", value: "合并单元格 " },
                        { range: "F2:F3", value: "合并单元格 " } 
                    ],
                    normalData: [
                        ["监督员地区", "总数", "有效", "", "有效政治类", ""],
                        ["平谷区监督员", "244", "", "197", "28", "169"],
                        ["", "244", "", "197", "28", ""],
                        ["数据 16", "数据 17", "数据 18", "数据 19", "数据 20"],
                        ["数据 21", "数据 22", "数据 23", "数据 24", "数据 25"],
                        ["", "", "", "", ""]
                    ],
                    occupiedCells: []
                };
            },
            computed: {
                // 生成完整表格数据
                tableData() {
                    const rows = this.normalData.length;
                    const cols = this.normalData[0].length;
                    const emptyTable = Array.from({ length: rows }, () => Array(cols).fill(null));
 
                    // 填充合并单元格
                    this.mergedCells.forEach(({ range, value }) => {
                        const [start, end] = range.split(':');
                        const startRow = parseInt(start.match(/\d+/)[0]) - 1;
                        const startCol = start.charCodeAt(0) - 65;
                        const endRow = parseInt(end.match(/\d+/)[0]) - 1;
                        const endCol = end.charCodeAt(0) - 65;
 
                        // 填充合并单元格的位置
                        for (let r = startRow; r <= endRow; r++) {
                            for (let c = startCol; c <= endCol; c++) {
                                if (r === startRow && c === startCol) {
                                    emptyTable[r][c] = value; // 合并单元格的值
                                } else {
                                    this.occupiedCells[r] = this.occupiedCells[r] || [];
                                    this.occupiedCells[r][c] = true; // 标记占用
                                }
                            }
                        }
                    });
 
                    // 填充普通单元格的数据
                    this.normalData.forEach((row, r) => {
                        row.forEach((cell, c) => {
                            if (!this.occupiedCells[r] || !this.occupiedCells[r][c]) {
                                emptyTable[r][c] = cell;
                            }
                        });
                    });
 
                    return emptyTable;
                }
            },
            methods: {
                isCellOccupied(rowIndex, colIndex) {
                    return this.occupiedCells[rowIndex] && this.occupiedCells[rowIndex][colIndex];
                },
                getCellValue(rowIndex, colIndex) {
                    return this.tableData[rowIndex][colIndex];
                },
                getRowSpan(rowIndex, colIndex) {
                    let rowspan = 1;
                    const firstValue = this.getCellValue(rowIndex, colIndex);
                    const mergedCell = this.mergedCells.find(({ range }) => {
                        const [start, end] = range.split(':');
                        const startRow = parseInt(start.match(/\d+/)[0]) - 1;
                        const startCol = start.charCodeAt(0) - 65;
                        const endRow = parseInt(end.match(/\d+/)[0]) - 1;
                        const endCol = end.charCodeAt(0) - 65;
                        return rowIndex === startRow && colIndex === startCol;
                    });
                    if (mergedCell) {
                        const [start, end] = mergedCell.range.split(':');
                        const startRow = parseInt(start.match(/\d+/)[0]) - 1;
                        const endRow = parseInt(end.match(/\d+/)[0]) - 1;
                        rowspan = endRow - startRow + 1;
                    }
                    return rowspan;
                },
                getColSpan(rowIndex, colIndex) {
                    let colspan = 1;
                    const firstValue = this.getCellValue(rowIndex, colIndex);
                    const mergedCell = this.mergedCells.find(({ range }) => {
                        const [start, end] = range.split(':');
                        const startRow = parseInt(start.match(/\d+/)[0]) - 1;
                        const startCol = start.charCodeAt(0) - 65;
                        const endRow = parseInt(end.match(/\d+/)[0]) - 1;
                        const endCol = end.charCodeAt(0) - 65;
                        return rowIndex === startRow && colIndex === startCol;
                    });
                    if (mergedCell) {
                        const [start, end] = mergedCell.range.split(':');
                        const startCol = start.charCodeAt(0) - 65;
                        const endCol = end.charCodeAt(0) - 65;
                        colspan = endCol - startCol + 1;
                    }
                    return colspan;
                },
                isMergedCell(rowIndex, colIndex) {
                    return this.mergedCells.some(({ range }) => {
                        const [start, end] = range.split(':');
                        const startRow = parseInt(start.match(/\d+/)[0]) - 1;
                        const startCol = start.charCodeAt(0) - 65;
                        return rowIndex === startRow && colIndex === startCol;
                    });
                }
            }
        });
    </script>
</body>
</html>

到此这篇关于Java实现解析Excel复杂表头的文章就介绍到这了,更多相关Java解析Excel内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文