网友作品 #000052 · 提交于 2026/08/06
造价工具箱:Excel材料清单模糊匹配回填上价工具



1 / 3
作品简介
造价工具箱 V0.3 是一款面向工程造价场景的 Excel 数据处理桌面工具,解决的核心问题是:目标表(A 表)与源数据表(B 表)文本写法不一致时,如何把 B 表的材料、综合单价等信息准确找回来填进 A 表,替代人工逐条检索核对。
工具提供两大功能模块:模糊匹配回填按多列加权相似度匹配两表数据,把 B 表指定列回填到 A 表;特征合并把多列特征数据拼接为带序号的格式化文本输出到指定列。
界面按 MVVM 架构组织,功能页一键切换,匹配规则通过弹窗可视化配置,发布为自包含运行包后拷贝到任何 Windows 电脑即可使用。
核心亮点
- 多列加权模糊匹配:基于 Levenshtein 编辑距离计算列相似度并加权平均,权重 0-100 步进 5、总和须为 100
- 规则全可视化配置:表头行号、A/B 表匹配列、权重映射、触发列、结果列、回填映射均可在设置弹窗中完成
- 点击即匹配:在 A 表触发列点击单元格即发起该行模糊匹配,结果弹窗展示 TopN 候选,确认后回填
- 溯源与批量:匹配结果所在的 B 表行号写入结果列便于核对溯源;支持配置「一同回填条件」,同条件行一键同回填
- 特征合并:勾选参与列、设置单位后,逐行拼接为「1、名称:×× 2、高度:××」式带序号文本,批量写入目标列
- 防御性读写:写入前检测文件是否被 Excel 占用,自动解除目标单元格合并区域,裁剪尾部空行,单表最大 10 万行
- 免安装运行:自包含发布为独立文件夹,拷贝即用,无需安装 .NET 运行时
技术与工具
- .NET 9 + WPF 桌面端,界面采用 HandyControl 控件库
- Excel 读写基于 EPPlus 8.6(.xlsx),读写均记录 Excel 真实行号避免表头偏移
- CommunityToolkit.Mvvm 源生成器支撑数据绑定,Serilog 按日期滚动记录日志
- 配置以 JSON 持久化在用户应用数据目录,含匹配配置、合并配置与最近文件
- 作者零基础,耗时约一周完成开发
开发过程
- 开发中沉淀了多条避坑经验:WPF 单文件发布与 HandyControl 主题资源不兼容会导致启动静默退出,改用多文件自包含发布解决
- 切换工作表后权重映射的列名字典未同步刷新会引发负索引崩溃,通过同步更新字典引用并过滤负数索引修复
- Excel 列名含括号、点号等特殊字符会导致 WPF 绑定失败,采用特殊字符替换加标题恢复的方案处理
适用场景
- 工程造价:工程清单中材料、综合单价的批量模糊匹配上价,减少人工检索核对
- 多源数据整合:不同 Excel 表格间非标准化文本的匹配关联与信息回填,如库存盘点、客户信息对账
原始提交内容(点开查看,可复制全文去问 AI)
造价工具箱 V0.3
基于 .NET 9 + WPF + EPPlus 的造价数据处理工具,支持模糊匹配回填与特征合并。
一、功能概览
| 功能 | 说明 |
|---|---|
| 模糊匹配回填 | A表(目标)与B表(源数据)按多列加权模糊匹配,将B表数据回填到A表 |
| 特征合并 | 将多列特征数据合并为带序号的格式化文本,输出到指定列 |
二、技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| .NET | 9.0 | 运行时框架 |
| WPF | — | 桌面UI |
| EPPlus | 8.6.0 | Excel读写(.xlsx) |
| HandyControl | 3.5.1 | WPF UI控件库 |
| CommunityToolkit.Mvvm | 8.4.2 | MVVM支持(源生成器) |
| Serilog | 4.3.1 | 日志记录 |
三、项目结构
src/CostToolbox/
App.xaml # 应用入口,HandyControl主题配置
CostToolbox.csproj # 项目文件
Models/ # 数据模型(纯POCO)
BackfillMapping.cs # 回填映射(A列B列)
CellWrite.cs # 单元格写入指令
ColumnWeightPair.cs # 列权重对(A列B列)
FeatureMergeConfig.cs # 特征合并配置
MatchConfig.cs # 模糊匹配配置
MatchGroup.cs # 匹配组(列选择+权重)
MatchResult.cs # 单条匹配结果
SheetData.cs # 工作表数据(含Excel真实行号)
Services/ # 业务服务层
ExcelService.cs # Excel读写(EPPlus封装)
FuzzyMatchService.cs # 模糊匹配核心(Levenshtein)
FeatureMergeService.cs# 特征合并核心
ConfigService.cs # JSON配置持久化
LogService.cs # Serilog日志初始化
I*.cs # 接口定义
ViewModels/ # MVVM视图模型
FuzzyMatchViewModel.cs# 模糊匹配页面VM
FeatureMergeViewModel.cs# 特征合并页面VM
MainViewModel.cs # 主窗口VM
Views/ # WPF视图
MainWindow.xaml # 主窗口(双视图切换)
MatchSettingDialog.xaml # 匹配设置弹窗
MatchResultDialog.xaml # 匹配结果弹窗
SaveConfirmDialog.xaml # 保存确认弹窗
Converters/ # 值转换器
四、核心逻辑详解
4.1 模糊匹配回填
匹配算法:Levenshtein编辑距离
// FuzzyMatchService.cs
public double LevenshteinSimilarity(string? a, string? b)
{
// 动态规划计算编辑距离
// 相似度 = 1 - distance / max(lenA, lenB)
return 1.0 - (double)distance / Math.Max(m, n);
}
核心流程:
-
配置匹配规则(
MatchSettingDialog)- 选择A表、B表的匹配列(可多选)
- 配置列权重映射(如:A表“名称”B表“项目名称”,权重40)
- 设置触发列(点击A表该列即触发匹配)
- 设置回填映射(将B表的哪些列回填到A表的哪些列)
-
加权匹配计算
// 对每对ColumnWeights计算加权平均相似度 double avgScore = totalWeightedSim / totalWeight; -
回填写入
- 匹配结果弹窗展示TopN候选
- 用户确认后,将B表指定列数据写入A表
- 同时写入B表行号到结果列,方便溯源
关键设计:Excel真实行号映射
// SheetData.cs
public class SheetData
{
public List<List<string>> Rows { get; } // 0-based索引数据
public List<int> ExcelRowNumbers { get; } // 对应的真实Excel行号(1-based)
}
_fullDataA.Rows[i] 是内存中的0-based索引,而 _fullDataA.ExcelRowNumbers[i] 是对应的Excel真实行号。所有回填操作都基于真实行号,避免表头行偏移错误。
4.2 特征合并
// FeatureMergeService.cs
public string MergeRow(List<string> row, List<string> headers,
List<int> paramCols, Dictionary<int, string> units, ...)
输出格式:
1、名称:丛生香樟
2、高度:750-850
3、冠幅:550-600
4、备注:丛生杆数5杆以上
5、其他未尽事宜,具体详见图纸。
流程:
- 勾选参与合并的列
- 为各列设置单位(可选,留空不追加)
- 指定输出目标列(如K列)
- 逐行拼接生成带序号文本,批量写入Excel
4.3 Excel读写服务(EPPlus 8)
// ExcelService.cs 核心方法
Task<ExcelFileInfo> OpenAsync(string filePath);
Task<SheetData> ReadAllRowsAsync(string filePath, string sheetName, int headerRow);
Task BatchBackfillAsync(string filePath, string sheetName, IEnumerable<CellWrite> writes);
防御性设计:
- 文件占用检测:任何写入前先以
FileShare.ReadWrite尝试打开,被Excel占用则立即抛异常 - 合并单元格处理:写入前自动解除目标单元格所在合并区域
- 尾部空行裁剪:读取时从
Dimension.Rows往回扫描,跳过尾部连续空行,避免格式残留导致卡死 - 行数上限:
ReadAllRowsAsync限制最大10万行
五、MVVM架构
5.1 数据绑定链
View (XAML) ViewModel (CommunityToolkit.Mvvm) Service Model
5.2 关键特性
[ObservableProperty]源生成器:编译期自动生成属性变更通知代码,无需手写INotifyPropertyChanged实现[RelayCommand]源生成器:自动将方法包装为ICommand,支持异步命令- Partial方法属性变更钩子:如
OnSelectedSheetAChanged在属性变化时自动触发加载
5.3 视图切换
MainWindow.xaml 中使用两个 Grid(FuzzyMatchContent / FeatureMergeContent)通过 Visibility 切换,共享同一个 DataContext 的不同子ViewModel:
FuzzyMatchPanel.DataContext = _fuzzyMatchVm;
FeatureMergePanel.DataContext = _featureMergeVm;
六、避坑指南
6.1 WPF单文件发布与HandyControl不兼容
问题:PublishSingleFile=true 时,HandyControl的 pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml 资源加载失败,程序启动后静默退出。
解决:取消单文件发布,使用多文件自包含发布。
6.2 权重映射行字典未同步更新
问题:切换工作表/表头行后,WeightMappingRow 内部的 _mapA/_mapB(列名索引字典)未同步刷新,导致返回 -1 索引,保存后匹配时 rowA[-1] 崩溃。
解决:RefreshColsA/B 方法中同步更新字典引用,保存前过滤负数索引。
6.3 DataGrid列名含特殊字符导致绑定失败
问题:Excel列名含 ()、[]、. 等特殊字符时,WPF PropertyPath 解析失败。
解决:SanitizeForBinding 方法将特殊字符替换为全角等价字符,同时用 DataColumn.Caption 保存原始标题,在 AutoGeneratingColumn 事件中恢复显示。
6.4 另一台电脑加载B表卡死
排查顺序:
- 确认B表未被Excel占用
- 确认文件为
.xlsx格式 - 检查是否缺少VC++运行库
- 检查文件是否含大量空行/格式行(已做尾部空行裁剪优化)
七、构建与发布
7.1 开发环境要求
- .NET 9 SDK
- Visual Studio 2022 或 VS Code + C# Dev Kit
- Windows 10/11
7.2 本地运行
cd src/CostToolbox
dotnet run
7.3 发布为独立运行包
dotnet publish -c Release --self-contained true -r win-x64 -o ./发布
发布后的 发布 文件夹可直接拷贝到任何Windows电脑运行,无需安装.NET运行时。
八、配置存储
配置以JSON格式存储在用户应用数据目录:
%LocalAppData%/CostToolbox/config.json
包含:
MatchConfig:模糊匹配配置(表头行、匹配列、权重、回填映射等)FeatureMergeConfig:特征合并配置RecentFile:最近打开的文件路径
九、日志
日志文件位于程序同目录 logs/ 下,按日期滚动:
logs/costtoolbox-20260613.log
使用Serilog File Sink,记录所有操作和异常堆栈,便于排查问题。
十、后续开发约定
已稳定的两个核心功能模块,后续新增功能时严禁改动:
- 模糊匹配回填:
FuzzyMatchService、FuzzyMatchViewModel、MatchSettingDialog - 特征合并:
FeatureMergeService、FeatureMergeViewModel、ExcelService读写接口
新增功能应独立建类/建页,避免影响已有稳定逻辑。
本作品归公众号 Excel催化剂 所有,仅限授权用户自用。