使用EPPlus库识别并高亮Excel表重复记录的技术问题
解决整行重复识别、高亮与批注的问题
我明白你的问题所在了——你当前的代码是在检查单个单元格值是否重复,而不是基于多列组合判断整行是否重复,这就是为什么它总是返回true的原因(比如不同行的FirstName可能有相同值,但这并不代表整行是重复记录)。我们来修正这个逻辑,实现你需要的功能:基于指定多列识别重复行,然后对整行应用高亮和批注。
原代码的核心问题
你的代码遍历了所有工作表的所有单元格,只要任何单元格的值在列表中存在就返回true,这完全不符合“整行重复”的判断逻辑。比如两个不同的员工可能有相同的名字,但地址不同,这时候你的代码会误判为重复。
正确的实现思路
要实现基于多列的整行重复判断,我们需要:
- 先确定用来判断重复的关键列组合(比如
FirstName、LastName、Address这三列) - 遍历数据行(跳过表头行),把每行的关键列值组合成一个唯一标识
- 用字典记录这个标识对应的行号,当遇到重复标识时,标记为重复行并处理
- 对重复行执行高亮整行、添加批注的操作
完整实现代码
下面是基于EPPlus的完整代码,包含重复行识别、高亮和批注功能:
using OfficeOpenXml; using OfficeOpenXml.Style; using System; using System.Collections.Generic; using System.IO; using System.Linq; public static bool IdentifyAndMarkDuplicateRows(string path) { bool hasDuplicates = false; // 设置EPPlus的License(EPPlus 5+需要,若用旧版本可注释) ExcelPackage.LicenseContext = LicenseContext.NonCommercial; using (ExcelPackage excel = new ExcelPackage(new FileInfo(path))) { // 定位到EmployeeSheet工作表 ExcelWorksheet sheet = excel.Workbook.Worksheets["EmployeeSheet"]; if (sheet == null) { throw new Exception("未找到EmployeeSheet工作表"); } // 1. 获取关键列的索引(假设第一行是表头) var headerRow = sheet.Cells[1, 1, 1, sheet.Dimension.End.Column]; int firstNameCol = headerRow.First(c => c.Value.ToString().Equals("FirstName", StringComparison.OrdinalIgnoreCase)).Start.Column; int lastNameCol = headerRow.First(c => c.Value.ToString().Equals("LastName", StringComparison.OrdinalIgnoreCase)).Start.Column; int addressCol = headerRow.First(c => c.Value.ToString().Equals("Address", StringComparison.OrdinalIgnoreCase)).Start.Column; // 2. 用字典存储已出现的行标识(关键列组合)和对应的行号 Dictionary<string, int> seenRows = new Dictionary<string, int>(); // 3. 遍历数据行(从第2行开始,跳过表头) int startRow = 2; int endRow = sheet.Dimension.End.Row; int endCol = sheet.Dimension.End.Column; for (int row = startRow; row <= endRow; row++) { // 获取当前行的关键列值,处理null情况 string firstName = sheet.Cells[row, firstNameCol].Value?.ToString() ?? string.Empty; string lastName = sheet.Cells[row, lastNameCol].Value?.ToString() ?? string.Empty; string address = sheet.Cells[row, addressCol].Value?.ToString() ?? string.Empty; // 组合成唯一标识(用特殊字符分隔,避免不同组合产生相同字符串) string rowIdentifier = string.Join("|", firstName, lastName, address); if (seenRows.ContainsKey(rowIdentifier)) { // 发现重复行,标记状态 hasDuplicates = true; int originalRow = seenRows[rowIdentifier]; // 高亮当前重复行 sheet.Cells[row, 1, row, endCol].Style.Fill.PatternType = ExcelFillStyle.Solid; sheet.Cells[row, 1, row, endCol].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightPink); // 高亮原始重复行(可选) sheet.Cells[originalRow, 1, originalRow, endCol].Style.Fill.PatternType = ExcelFillStyle.Solid; sheet.Cells[originalRow, 1, originalRow, endCol].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightPink); // 给当前重复行添加批注 ExcelComment comment = sheet.Cells[row, firstNameCol].Comment ?? sheet.Cells[row, firstNameCol].AddComment(string.Empty, "Duplicate Check"); comment.Text = $"此行为重复记录,原始行位于第{originalRow}行"; comment.AutoFit = true; } else { // 记录新的行标识 seenRows.Add(rowIdentifier, row); } } // 保存修改 excel.Save(); } return hasDuplicates; }
代码关键点说明
- 关键列定位:通过表头名称动态查找列索引,避免硬编码列号,适配不同的表格结构
- 行标识生成:用
|分隔关键列值,确保不同的列组合不会产生相同的标识(如果你的数据中包含|,可以换成其他特殊字符) - 重复处理:不仅标记当前重复行,还可以选择高亮原始重复行,批注中明确指出原始行位置
- 空值处理:用
?? string.Empty把null值转为空字符串,避免组合标识时出现null异常 - 样式设置:使用EPPlus的Style API设置整行填充色,实现高亮效果
使用注意事项
- 确保你的项目已引用EPPlus NuGet包(可以通过NuGet安装:
Install-Package EPPlus) - 若使用EPPlus 5及以上版本,需要设置LicenseContext(代码中已包含)
- 确保表头行的列名与代码中指定的一致(大小写不敏感)
- 如果需要基于更多列判断重复,只需要添加对应的列索引获取和标识组合即可
内容的提问来源于stack exchange,提问作者Koder
相关产品推荐
相关产品推荐

