java实现对excel文件合并单元格(只针对文件)

建议使用quickMergeWithDependency方法,手动添加合并规则

filePath Excel文件路径

brokerColumn 合并参考标准:如根据名称合并,正好在B列,那么合并的时候都参考该列,不会跨行

mergeColumns 需要合并的列(字母表示,如"A,C,D,E,F,G,H,I,N")不能包含brokerColumn否则会报错

startRow 起始行(从0开始)从哪行开始合并

dependencyRules 依赖规则,格式:"H:G,N:G" 表示H列依赖G列,N列依赖G列(G列相同才合并后面H或N列相同的值)

java 复制代码
package org.springblade.modules.api.utils;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.*;
import java.util.*;

public class ExcelMergeByBroker {
    
    /**
     * 根据水产品经纪人列进行合并
     * @param filePath Excel文件路径
     * @param sheetIndex 工作表索引
     * @param brokerColumnIndex 水产品经纪人列索引(从0开始)
     * @param mergeColumnIndexes 需要合并的列索引数组
     * @param startRow 起始行索引(0开始,通常是表头之后的数据起始行)
     * @param specialRules 特殊合并规则
     */
    public static void mergeByBroker(String filePath, int sheetIndex, int brokerColumnIndex, 
                                     int[] mergeColumnIndexes, int startRow, Map<Integer, Integer> specialRules) {
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            Workbook workbook = WorkbookFactory.create(fis);
            Sheet sheet = workbook.getSheetAt(sheetIndex);
            DataFormatter formatter = new DataFormatter();
            
            int lastRow = sheet.getLastRowNum();
            
            // 1. 先合并水产品经纪人列
            List<int[]> brokerMergeRanges = mergeBrokerColumn(sheet, formatter, brokerColumnIndex, startRow, lastRow);
            
            // 2. 根据经纪人的合并范围,合并其他列
            for (int[] range : brokerMergeRanges) {
                int start = range[0];
                int end = range[1];
                int brokerRowCount = end - start + 1; // 经纪人合并的行数
                
                if (brokerRowCount > 1) {
                    // 对每一列,在经纪人合并的范围内检查是否需要合并
                    for (int colIndex : mergeColumnIndexes) {
                        // 检查是否有特殊合并规则
                        if (specialRules != null && specialRules.containsKey(colIndex)) {
                            int dependentColumn = specialRules.get(colIndex);
                            mergeColumnWithDependency(sheet, formatter, colIndex, start, end, dependentColumn);
                        } else {
                            mergeColumnInRange(sheet, formatter, colIndex, start, end);
                        }
                    }
                }
            }
            
            fis.close();
            FileOutputStream fos = new FileOutputStream(file);
            workbook.write(fos);
            fos.close();
            workbook.close();
            System.out.println("根据经纪人合并完成!");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 原始方法,兼容旧调用
     */
    public static void mergeByBroker(String filePath, int sheetIndex, int brokerColumnIndex, 
                                     int[] mergeColumnIndexes, int startRow) {
        mergeByBroker(filePath, sheetIndex, brokerColumnIndex, mergeColumnIndexes, startRow, null);
    }
    
    /**
     * 合并水产品经纪人列,并返回合并范围列表
     */
    private static List<int[]> mergeBrokerColumn(Sheet sheet, DataFormatter formatter, 
                                                 int brokerColumnIndex, int startRow, int lastRow) {
        List<int[]> mergeRanges = new ArrayList<>();
        int currentStart = startRow;
        String currentValue = "";
        
        for (int i = startRow; i <= lastRow; i++) {
            Row row = sheet.getRow(i);
            String brokerValue = getCellValue(row, brokerColumnIndex, formatter);
            
            if (i == startRow) {
                // 第一行
                currentValue = brokerValue;
                currentStart = i;
            } else if (!brokerValue.equals(currentValue)) {
                // 经纪人发生变化,记录上一个范围
                if (i - 1 > currentStart) { // 至少有两行相同的经纪人
                    mergeRanges.add(new int[]{currentStart, i - 1});
                    CellRangeAddress region = new CellRangeAddress(currentStart, i - 1, brokerColumnIndex, brokerColumnIndex);
                    sheet.addMergedRegion(region);
                }
                currentValue = brokerValue;
                currentStart = i;
            }
            
            // 处理最后一行
            if (i == lastRow && i > currentStart) {
                mergeRanges.add(new int[]{currentStart, i});
                CellRangeAddress region = new CellRangeAddress(currentStart, i, brokerColumnIndex, brokerColumnIndex);
                sheet.addMergedRegion(region);
            }
        }
        
        return mergeRanges;
    }
    
    /**
     * 在指定范围内合并列
     */
    private static void mergeColumnInRange(Sheet sheet, DataFormatter formatter, 
                                          int colIndex, int startRow, int endRow) {
        int mergeStart = startRow;
        String prevValue = getCellValue(sheet.getRow(startRow), colIndex, formatter);
        
        for (int i = startRow + 1; i <= endRow; i++) {
            Row row = sheet.getRow(i);
            String currentValue = getCellValue(row, colIndex, formatter);
            
            if (!currentValue.equals(prevValue)) {
                // 值发生变化,合并之前的区域
                if (i - 1 > mergeStart) { // 至少有2行相同
                    CellRangeAddress region = new CellRangeAddress(mergeStart, i - 1, colIndex, colIndex);
                    sheet.addMergedRegion(region);
                }
                mergeStart = i;
                prevValue = currentValue;
            }
            
            // 处理范围内的最后一行
            if (i == endRow && i > mergeStart) {
                CellRangeAddress region = new CellRangeAddress(mergeStart, i, colIndex, colIndex);
                sheet.addMergedRegion(region);
            }
        }
    }
    
    /**
     * 带依赖关系的列合并(例如:H列依赖G列,N列依赖G列)
     */
    private static void mergeColumnWithDependency(Sheet sheet, DataFormatter formatter, 
                                                 int colIndex, int startRow, int endRow, int dependentColumn) {
        int mergeStart = startRow;
        String prevValue = getCellValue(sheet.getRow(startRow), colIndex, formatter);
        String prevDependentValue = getCellValue(sheet.getRow(startRow), dependentColumn, formatter);
        
        for (int i = startRow + 1; i <= endRow; i++) {
            Row row = sheet.getRow(i);
            String currentValue = getCellValue(row, colIndex, formatter);
            String currentDependentValue = getCellValue(row, dependentColumn, formatter);
            
            // 当列值变化 或 依赖列值变化时,都需要重新开始合并范围
            boolean shouldBreakMerge = !currentValue.equals(prevValue) || 
                                       !currentDependentValue.equals(prevDependentValue);
            
            if (shouldBreakMerge) {
                // 值发生变化,合并之前的区域
                if (i - 1 > mergeStart) { // 至少有2行相同
                    CellRangeAddress region = new CellRangeAddress(mergeStart, i - 1, colIndex, colIndex);
                    sheet.addMergedRegion(region);
                }
                mergeStart = i;
                prevValue = currentValue;
                prevDependentValue = currentDependentValue;
            }
            
            // 处理范围内的最后一行
            if (i == endRow && i > mergeStart) {
                CellRangeAddress region = new CellRangeAddress(mergeStart, i, colIndex, colIndex);
                sheet.addMergedRegion(region);
            }
        }
    }
    
    /**
     * 根据水产品经纪人合并指定列(自动检测需要合并的列)
     * @param filePath Excel文件路径
     * @param sheetIndex 工作表索引
     * @param brokerColumnIndex 水产品经纪人列索引
     * @param startRow 起始行
     * @param allColumns 是否合并所有列(除了经纪人列)
     */
    public static void mergeByBrokerAll(String filePath, int sheetIndex, int brokerColumnIndex, 
                                        int startRow, boolean allColumns) {
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            Workbook workbook = WorkbookFactory.create(fis);
            Sheet sheet = workbook.getSheetAt(sheetIndex);
            
            int lastRow = sheet.getLastRowNum();
            Row firstRow = sheet.getRow(startRow);
            int lastColumn = firstRow.getLastCellNum() - 1; // 最后一列的索引
            
            // 确定需要合并的列
            List<Integer> mergeColumns = new ArrayList<>();
            
            if (allColumns) {
                // 合并除了经纪人列之外的所有列
                for (int i = 0; i <= lastColumn; i++) {
                    if (i != brokerColumnIndex) {
                        mergeColumns.add(i);
                    }
                }
            } else {
                // 合并指定列
                // 根据图片,可能需要合并的列:来源地、养殖主体信息、收购信息、承诺达标合格证、运输车辆等
                // 这里需要根据实际情况调整
            }
            
            // 转换为数组
            int[] mergeColumnArray = mergeColumns.stream().mapToInt(Integer::intValue).toArray();
            
            fis.close();
            workbook.close();
            
            // 调用合并方法
            mergeByBroker(filePath, sheetIndex, brokerColumnIndex, mergeColumnArray, startRow, null);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 智能合并:根据水产品经纪人自动合并相关列
     * @param filePath Excel文件路径
     * @param brokerColumn 经纪人列(B列=1)
     * @param startRow 数据起始行(从0开始)
     */
    public static void smartMergeByBroker(String filePath, int brokerColumn, int startRow) {
        // 根据图片中的列结构,设置需要合并的列
        // 假设经纪人列是B列(1),那么需要合并的列是:
        // C列(2)来源地, D列(3)单位或个人, E列(4)联系电话, F列(5)启运日期, 
        // G列(6)水产品种, H列(7)数量(公斤), I列(8)开具人, 
        // J列(9)车牌号, K列(10)驾驶员
        int[] mergeColumns = {2, 3, 4, 5, 6, 7, 8, 9, 10};
        
        // 特殊合并规则:H列(7)依赖G列(6),N列(13)依赖G列(6)
        Map<Integer, Integer> specialRules = new HashMap<>();
        specialRules.put(7, 6);  // H列依赖G列
        specialRules.put(13, 6); // N列依赖G列
        
        mergeByBroker(filePath, 0, brokerColumn, mergeColumns, startRow, specialRules);
    }
    
    /**
     * 获取单元格值
     */
    private static String getCellValue(Row row, int colIndex, DataFormatter formatter) {
        if (row == null) return "";
        Cell cell = row.getCell(colIndex);
        if (cell == null) return "";
        return formatter.formatCellValue(cell);
    }
    
    /**
     * 快速合并方法(最简单调用方式)
     * @param filePath Excel文件路径
     * @param brokerColumn 经纪人列(字母表示,如"B")
     * @param mergeColumns 需要合并的列(字母表示,如"C,D,E,F,G,H,I,J,K")
     * @param startRow 起始行(从0开始)
     */
    public static void quickMerge(String filePath, String brokerColumn, String mergeColumns, int startRow) {
        int brokerIndex = convertColumnLetterToIndex(brokerColumn);
        int[] mergeIndices = convertColumnStringToArray(mergeColumns);
        
        // 默认的特殊合并规则:H列(7)依赖G列(6),N列(13)依赖G列(6)
        Map<Integer, Integer> specialRules = new HashMap<>();
        
        // 检查是否包含H列(7)和N列(13)
        int gIndex = convertColumnLetterToIndex("G");
        int hIndex = convertColumnLetterToIndex("H");
        int nIndex = convertColumnLetterToIndex("N");
        
        // 如果合并列中包含H列,则设置H列依赖G列
        if (arrayContains(mergeIndices, hIndex)) {
            specialRules.put(hIndex, gIndex);
        }
        
        // 如果合并列中包含N列,则设置N列依赖G列
        if (arrayContains(mergeIndices, nIndex)) {
            specialRules.put(nIndex, gIndex);
        }
        
        // 如果没有特殊规则,specialRules会是空Map
        mergeByBroker(filePath, 0, brokerIndex, mergeIndices, startRow, specialRules.isEmpty() ? null : specialRules);
    }
    
    /**
     * 检查数组是否包含某个值
     */
    private static boolean arrayContains(int[] array, int value) {
        for (int item : array) {
            if (item == value) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 高级快速合并方法,可指定依赖规则
     * @param filePath Excel文件路径
     * @param brokerColumn 经纪人列(字母表示,如"B")
     * @param mergeColumns 需要合并的列(字母表示,如"A,C,D,E,F,G,H,I,N")
     * @param startRow 起始行(从0开始)
     * @param dependencyRules 依赖规则,格式:"H:G,N:G" 表示H列依赖G列,N列依赖G列
     */
    public static void quickMergeWithDependency(String filePath, String brokerColumn, String mergeColumns, 
                                               int startRow, String dependencyRules) {
        int brokerIndex = convertColumnLetterToIndex(brokerColumn);
        int[] mergeIndices = convertColumnStringToArray(mergeColumns);
        
        Map<Integer, Integer> specialRules = parseDependencyRules(dependencyRules);
        
        mergeByBroker(filePath, 0, brokerIndex, mergeIndices, startRow, 
                     specialRules.isEmpty() ? null : specialRules);
    }
    
    /**
     * 解析依赖规则字符串
     * @param dependencyRules 格式:"H:G,N:G" 或 "H:G" 或 "N:G"
     * @return 依赖规则Map
     */
    private static Map<Integer, Integer> parseDependencyRules(String dependencyRules) {
        Map<Integer, Integer> rules = new HashMap<>();
        
        if (dependencyRules == null || dependencyRules.trim().isEmpty()) {
            return rules;
        }
        
        String[] rulePairs = dependencyRules.split(",");
        for (String pair : rulePairs) {
            String[] parts = pair.split(":");
            if (parts.length == 2) {
                int targetCol = convertColumnLetterToIndex(parts[0].trim());
                int dependentCol = convertColumnLetterToIndex(parts[1].trim());
                rules.put(targetCol, dependentCol);
            }
        }
        
        return rules;
    }
    
    /**
     * 将列字母转换为索引
     */
    private static int convertColumnLetterToIndex(String columnLetter) {
        columnLetter = columnLetter.toUpperCase();
        int index = 0;
        for (int i = 0; i < columnLetter.length(); i++) {
            char c = columnLetter.charAt(i);
            index = index * 26 + (c - 'A' + 1);
        }
        return index - 1;
    }
    
    /**
     * 将列字符串转换为索引数组
     */
    private static int[] convertColumnStringToArray(String columns) {
        String[] parts = columns.split(",");
        int[] result = new int[parts.length];
        for (int i = 0; i < parts.length; i++) {
            result[i] = convertColumnLetterToIndex(parts[i].trim());
        }
        return result;
    }
    
    /**
     * 高级合并:可以根据配置灵活合并
     * @param filePath Excel文件路径
     * @param config 合并配置
     */
    public static void advancedMerge(String filePath, MergeConfig config) {
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            Workbook workbook = WorkbookFactory.create(fis);
            Sheet sheet = workbook.getSheetAt(config.getSheetIndex());
            DataFormatter formatter = new DataFormatter();
            
            int lastRow = sheet.getLastRowNum();
            Map<String, List<int[]>> brokerGroups = new HashMap<>();
            
            // 1. 按经纪人分组
            for (int i = config.getStartRow(); i <= lastRow; i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;
                
                String brokerValue = getCellValue(row, config.getBrokerColumnIndex(), formatter);
                if (brokerValue == null || brokerValue.trim().isEmpty()) {
                    continue;
                }
                
                brokerGroups.putIfAbsent(brokerValue, new ArrayList<>());
                brokerGroups.get(brokerValue).add(new int[]{i, i});
            }
            
            // 2. 合并每个经纪人的数据
            for (Map.Entry<String, List<int[]>> entry : brokerGroups.entrySet()) {
                List<int[]> rowRanges = entry.getValue();
                
                // 合并每个经纪人的行范围
                for (int[] range : rowRanges) {
                    int start = range[0];
                    int end = range[1];
                    
                    if (end - start + 1 > 1) {
                        // 合并经纪人列
                        CellRangeAddress brokerRegion = new CellRangeAddress(start, end, 
                            config.getBrokerColumnIndex(), config.getBrokerColumnIndex());
                        sheet.addMergedRegion(brokerRegion);
                        
                        // 合并其他列
                        for (int colIndex : config.getMergeColumnIndices()) {
                            // 检查是否有特殊依赖规则
                            if (config.getSpecialRules() != null && config.getSpecialRules().containsKey(colIndex)) {
                                int dependentColumn = config.getSpecialRules().get(colIndex);
                                mergeColumnWithDependency(sheet, formatter, colIndex, start, end, dependentColumn);
                            } else {
                                mergeColumnInRange(sheet, formatter, colIndex, start, end);
                            }
                        }
                    }
                }
            }
            
            fis.close();
            FileOutputStream fos = new FileOutputStream(file);
            workbook.write(fos);
            fos.close();
            workbook.close();
            System.out.println("高级合并完成!");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 合并配置类
     */
    public static class MergeConfig {
        private int sheetIndex = 0;
        private int brokerColumnIndex = 1; // B列
        private int[] mergeColumnIndices = {2, 3, 4, 5, 6, 7, 8, 9, 10}; // C-K列
        private int startRow = 2; // 从第3行开始(0-based)
        private Map<Integer, Integer> specialRules; // 特殊合并规则
        
        public MergeConfig() {}
        
        public MergeConfig(int brokerColumnIndex, int[] mergeColumnIndices, int startRow) {
            this.brokerColumnIndex = brokerColumnIndex;
            this.mergeColumnIndices = mergeColumnIndices;
            this.startRow = startRow;
        }
        
        // Getters and Setters
        public int getSheetIndex() { return sheetIndex; }
        public void setSheetIndex(int sheetIndex) { this.sheetIndex = sheetIndex; }
        
        public int getBrokerColumnIndex() { return brokerColumnIndex; }
        public void setBrokerColumnIndex(int brokerColumnIndex) { this.brokerColumnIndex = brokerColumnIndex; }
        
        public int[] getMergeColumnIndices() { return mergeColumnIndices; }
        public void setMergeColumnIndices(int[] mergeColumnIndices) { this.mergeColumnIndices = mergeColumnIndices; }
        
        public int getStartRow() { return startRow; }
        public void setStartRow(int startRow) { this.startRow = startRow; }
        
        public Map<Integer, Integer> getSpecialRules() { return specialRules; }
        public void setSpecialRules(Map<Integer, Integer> specialRules) { this.specialRules = specialRules; }
        
        /**
         * 添加特殊合并规则
         * @param targetCol 目标列(需要合并的列)
         * @param dependentCol 依赖列
         */
        public void addSpecialRule(int targetCol, int dependentCol) {
            if (specialRules == null) {
                specialRules = new HashMap<>();
            }
            specialRules.put(targetCol, dependentCol);
        }
    }
    
    /**
     * 主方法测试
     */
    public static void main(String[] args) {
        String filePath = "C:\\Users\\Administrator\\Desktop\\23460685336400AgentWater.xlsx";
        
        
        
        // 方法2:使用快速合并(自动检测H列和N列依赖G列)
        System.out.println("方法2:快速合并");
        quickMerge(filePath, "B", "A,C,D,E,F,G,H,I,N", 2);
        
        // 方法3:使用快速合并并指定依赖规则
        System.out.println("方法3:快速合并(带依赖规则)");
        quickMergeWithDependency(filePath, "B", "A,C,D,E,F,G,H,I,N", 2, "H:G,N:G");
        
        // 方法4:使用高级合并
        System.out.println("方法4:高级合并");
        MergeConfig config = new MergeConfig(1, new int[]{0, 2, 3, 4, 5, 6, 7, 8, 13}, 2);
        // 添加特殊规则:H列(7)依赖G列(6),N列(13)依赖G列(6)
        config.addSpecialRule(7, 6);
        config.addSpecialRule(13, 6);
        advancedMerge(filePath, config);
    }
}
相关推荐
no24544102 小时前
RAGFlow 全面接入 MinerU 2.0,支持 pipeline、vlm-transformers、vlm-sglang 三种模式,解析精度大幅度up
java·大数据·人工智能·python·ai·sglang
lkbhua莱克瓦242 小时前
MySQL事务隔离级别:从并发混乱到数据一致性守护者
java·数据库·mysql·隔离级别
lihao lihao2 小时前
C++ set和map
开发语言·c++·算法
小陈phd2 小时前
langGraph从入门到精通(三)——基于LangGraph的智能问答系统开发:Python单代理架构实战
开发语言·python·架构
Mr -老鬼2 小时前
Java、Go、Rust高并发时代“称雄”之战:场景适配与生态博弈
java·golang·rust
vivo互联网技术2 小时前
vivo 微服务架构实践之 Dubbo 性能优化
java·后端·微服务·中间件·dubbo
仙俊红2 小时前
Spring 构造器注入 vs 字段注入
java·后端·spring
iAkuya2 小时前
(leetcode)力扣100 40二叉树的直径(迭代递归)
java·算法·leetcode