有段时间接了个内部工具需求:要批量把一摞Word格式的报价单、设备清单自动转成Excel汇总表。刚开始我满脑子都是正经的表格解析、单元格映射,结果在会议室里被一个用惯了Office的老师傅一句话点醒——“你直接用Word另存成网页,Excel再打开不就行了嘛,格式全给你留着。”
这句话后来成了我这个工具的核心思路。今天就把这套“使用C#实现Word转Excel并保留格式”的三步方案完整拆开讲清楚,包括为什么这么设计、代码怎么写、哪些地方容易翻车,以及我在实际项目中踩过的坑。这套方案对经常处理办公文档自动化的C#开发人员尤其实用,不需要买商业组件,完全基于Office本身的能力,代码量少到惊人。
1. 方案选型:为什么选择“Word → HTML → Excel”这条路径
1.1 先盘点常见的 Word 转 Excel 技术路线
网上搜“C# Word转Excel”,能搜出一堆方案,但真正落过地的没几个。我先把主流路线摆出来对比一下,你就能理解为什么最后选了HTML中间格式。
第一种是Office Interop直接控制,在Word里打开文档、全选复制,再切到Excel粘贴。这个方案格式保留确实好,但Word和Excel两个进程同时跑,内存占用高得吓人,而且后台剪贴板操作极其不稳定,稍微遇到一个复杂文档就容易卡死或粘贴错位。
第二种是用Aspose.Words加Aspose.Cells,或者Spire.Doc加Spire.XLS。这两个商业库确实强,格式还原度非常高,也不需要装Office,但都要花钱买授权,Aspose全家桶价格不便宜,Spire虽然有个免费版,但对文档段落数、表格数有限制,超出部分会截断或者提示授权,不适合生产环境。
第三种是走OpenXML SDK加NPOI,把Word里的表格数据读出来再逐行写入Excel。这条路最“程序员思维”,但问题也很直接:Word里的表格结构远比xlsx里的表格复杂,合并单元格、嵌套表格、段落格式、图片混排,用OpenXML解析一遍会写到怀疑人生,而且格式基本保留不了。
第四种就是我用的这条路——Word先另存为HTML,再用Excel打开这个HTML,最后另存为xlsx。为什么可行?因为Microsoft Office的组件之间有一种天然的“血缘关系”:Word另存的HTML表格,Excel能原生识别并重建样式。整个过程不需要处理表格结构,不需要做样式映射,格式层面的东西Office自己内部消化掉了,我们只需要写三步调用代码。
| 方案 | 格式保留程度 | 是否需要安装Office | 成本 | 代码复杂度 |
|---|---|---|---|---|
| Interop直接复制粘贴 | 高 | 需要 | 免费 | 中高,不稳定 |
| Aspose / Spire | 高 | 不需要 | 收费 | 低 |
| OpenXML + NPOI | 低 | 不需要 | 免费 | 很高 |
| Word→HTML→Excel(本文方案) | 高 | 需要 | 免费 | 很低 |
1.2 三步方案的整体设计思路
这个方案的“三步”不是噱头,拆开就是三次Office API调用:
第一步,用Word.Application打开docx文档,另存为Filtered HTML(过滤格式的网页文件)。第二步,用Excel.Application打开刚才那个HTML文件,此时Word表格已经被Excel以原生表格形式重建了。第三步,把Excel工作簿另存为xlsx格式,关闭进程。
为什么HTML能做中间格式?核心原因在于HTML的table结构本身就是一种“半成品Excel”。Word里的表格在另存为HTML时,会把单元格内容、合并范围、边框、底色、字体颜色等写成table、td标签和内联style样式,这些结构恰好是Excel能直接吃进去的格式。简单说,Word转HTML等于把表格“摊开”成一种通用语言,Excel再“读”回这种语言,比自己解析Word对象模型要可靠得多。
当时测试的几十个样本文档里,包括带复杂表头的合同清单、带图片的报价单、带多级合并单元格的设备台账,用这个方法转换后,整体布局和单元格合并基本都能保留。尤其是表格线、字体颜色、单元格底色这些Excel用户最在意的“脸面”信息,还原度远超我的预期。
2. 前置环境与基础代码骨架
2.1 环境准备
先说运行时环境。这个方案依赖Office COM组件,所以目标机器必须安装Microsoft Office,Word和Excel都得装。版本方面,Office 2016、2019、365我都实测过,行为一致。开发环境我建议用Visual Studio 2022,项目框架选.NET Framework 4.8,别选.NET 6或.NET 8——虽然新框架也能引用COM类型,但会遇到“无法嵌入互操作类型”的报错,处理起来额外费劲,桌面工具场景没必要给自己找麻烦。
创建项目后,需要手动添加两个COM引用。在解决方案资源管理器里右键“引用”→“添加引用”→“COM”,勾选:
- Microsoft Word 16.0 Object Library
- Microsoft Excel 16.0 Object Library
如果你的Office是32位,这里可能显示15.0或14.0,不影响使用。添加完成后,到引用列表里选中这两个COM引用,把“嵌入互操作类型”改成False。这是个容易踩的坑:如果不改,代码里Word.Application和Excel.Application的接口类型会在运行时出现版本转换问题,有时候编译能过但执行就抛异常。
操作系统注意一下位数。老板的电脑装了64位Office,你的开发机是32位Office,编译出来的程序在对方机器上可能直接报“检索 COM 类工厂中 CLSID 为 {000209FF-0000-0000-C000-000000000046} 的组件时失败”。建议把项目平台目标统一设为x64,并要求目标机器安装64位Office,省得后续排查进程崩溃问题。
2.2 三步核心代码实现
代码结构不复杂,核心逻辑就三大段。先看完整的转换函数:
using System; using System.IO; using System.Runtime.InteropServices; using Word = Microsoft.Office.Interop.Word; using Excel = Microsoft.Office.Interop.Excel; public static class WordToExcelConverter { public static void Convert(string wordFilePath, string excelFilePath) { string htmlPath = Path.Combine( Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".html"); Word.Application wordApp = null; Word.Document wordDoc = null; Excel.Application excelApp = null; Excel.Workbook excelWorkbook = null; try { // 第一步:Word打开文档,另存为HTML wordApp = new Word.Application(); wordApp.Visible = false; wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone; wordDoc = wordApp.Documents.Open( wordFilePath, ReadOnly: true, Visible: false); wordDoc.SaveAs2( FileName: htmlPath, FileFormat: Word.WdSaveFormat.wdFormatFilteredHTML, Encoding: Word.msoEncoding.msoEncodingUTF8); wordDoc.Close(Word.WdSaveOptions.wdDoNotSaveChanges); // 第二步:Excel打开HTML excelApp = new Excel.Application(); excelApp.Visible = false; excelApp.DisplayAlerts = false; excelApp.ScreenUpdating = false; excelWorkbook = excelApp.Workbooks.Open(htmlPath); Excel.Worksheet worksheet = (Excel.Worksheet)excelWorkbook.ActiveSheet; worksheet.Columns.AutoFit(); // 第三步:另存为xlsx并关闭 excelWorkbook.SaveAs2( excelFilePath, Excel.XlFileFormat.xlOpenXMLWorkbook); excelWorkbook.Close(false); } finally { if (wordDoc != null) Marshal.FinalReleaseComObject(wordDoc); if (excelWorkbook != null) Marshal.FinalReleaseComObject(excelWorkbook); if (wordApp != null) { wordApp.Quit(); Marshal.FinalReleaseComObject(wordApp); } if (excelApp != null) { excelApp.Quit(); Marshal.FinalReleaseComObject(excelApp); } if (File.Exists(htmlPath)) { try { File.Delete(htmlPath); } catch { } string htmlFolder = Path.Combine( Path.GetDirectoryName(htmlPath), Path.GetFileNameWithoutExtension(htmlPath) + "_files"); if (Directory.Exists(htmlFolder)) { try { Directory.Delete(htmlFolder, true); } catch { } } } } } }执行流程不复杂:wordApp.Documents.Open打开目标文档,SaveAs2保存为Filtered HTML;excelApp.Workbooks.Open直接读这个HTML文件;最后SaveAs2导出为xlsx。我这里用了一个临时HTML文件路径加GUID文件名,避免多人同时跑工具时互相覆盖,这个细节在实际办公场景里经常被忽略。
注意SaveAs2里的Encoding参数,我传的是msoEncodingUTF8。这个很关键,如果Word文档里包含中文、特殊符号,默认编码容易出现乱码,UTF8能保证Excel打开时中文正常显示。FileFormat选wdFormatFilteredHTML而不是wdFormatHTML,两者的差别下面专门讲。
2.3 为什么需要手动清理Office进程
用过Office Interop的老手都知道,这玩意儿最大的麻烦不是写逻辑,而是程序退出后一堆WINWORD.EXE和EXCEL.EXE赖在任务管理器里不走。我最初写第一版工具时没太在意释放,结果测试跑了几十次之后,客户的电脑卡到鼠标都挪不动,任务管理器里躺着三十多个Word进程。
原因在于.NET调用COM对象时,每一个接口引用都会增加一次引用计数,只有引用计数归零后进程才会真正退出。代码里的wordDoc、excelWorkbook这些局部变量,如果只是让它自然离开作用域,RCW不会立刻释放,必须显式调用Marshal.FinalReleaseComObject。而且释放顺序也有讲究:先释放Document,再释放Application对象,最后调Quit,顺序反了容易出现二次弹窗或进程崩溃。
还有一个很多人忽略的坑:Quit之后进程不一定立刻消失,需要给系统一点反应时间。我习惯在Quit之后加一句GC.Collect()和GC.WaitForPendingFinalizers(),虽然这做法在高端程序员眼里不太体面,但在桌面工具场景下确实能显著减少进程残留。网上很多人教直接Process.Kill把WINWORD.EXE全杀光,我强烈不推荐——万一用户自己开着正在编辑的Word文档,你把进程一杀,人家辛苦写的文档直接没保存,这锅你背不起。正确做法是转换前记录已存在的Word、Excel进程ID,转换后只清理新冒出来的进程,这个技巧后面在排查章节给完整代码。
3. 保留格式的关键细节与参数解析
3.1 另存为HTML时关键的编码与格式参数
Word的另存为HTML有两个选项:wdFormatHTML(完整HTML)和wdFormatFilteredHTML(筛选后的HTML)。这两个的区别很多人搞不清楚。wdFormatHTML是Word自己的网页格式,里面会包含大量Office特有的标记,文件体积大,而且Excel打开时经常弹“文件格式与扩展名不匹配”的提示。wdFormatFilteredHTML是经过过滤的干净HTML,里面只保留页面展示需要的标签和样式,Excel打开最顺畅,我用它处理几百个文档没遇到过一次弹窗。
还有一个细节是编码。这个方法里用了命名参数Encoding来指定编码为UTF8。如果不指定,默认可能是ANSI或者系统区域编码,遇到文档里有中文引号、拼音注音、特殊符号时,生成出来的HTML可能在某一段被截断,Excel打开后那一行之后的表格内容全部错位。这个问题非常隐蔽,我当时排查了大半天,最后用浏览器打开中间HTML才发现是编码问题。
再说一下图片资源。Word另存为HTML时,如果文档里有图片,会自动在HTML同目录下生成一个以HTML文件名命名的_files文件夹,图片全部存在里面。文件格式是相对路径引用。我们转换完成后,一定记得把这个文件夹一起清理掉,否则C盘临时目录会积累一堆垃圾图片文件。我在上面的代码里就做了这个清理动作。
3.2 Excel打开HTML后的二次处理
Excel直接打开HTML后,大部分格式能保留,但有一件事它不会替你干——自动调整列宽。Word里的表格列宽是基于页面宽度计算的,到了Excel里,很多列会显得过窄或者过宽,尤其是有合并单元格的列,字都叠在一起。所以打开HTML后,我一般会先调用worksheet.Columns.AutoFit(),让Excel根据内容自动撑开列宽。
如果你的场景需要更进一步的规整,可以考虑增加这样几行:
// 设置数据区域加边框 Excel.Range usedRange = worksheet.UsedRange; usedRange.Borders.LineStyle = Excel.XlLineStyle.xlContinuous; usedRange.Borders.Weight = Excel.XlBorderWeight.xlThin; // 首行加粗并居中 Excel.Range headerRow = worksheet.Rows[1]; headerRow.Font.Bold = true; headerRow.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;要注意的是,不是所有文档都适合加边框。如果Word文档里本身布局很宽松、留白很多,强行加边框反而显得杂乱。我的经验是:纯数据类表格(设备清单、报价单)加边框效果好,宣传册类、图文混排类的文档就别动样式了,保持原样即可。
分页问题也需要提前考虑。Word里一页A4纸的内容,转成Excel后实际占用多少行取决于字体和列宽。有些文档转出来超过20页,导出xlsx后打印布局非常乱。这时候可以在Excel里设置打印区域或调整页面缩放。代码里做这类操作也不难,比如把整个工作表缩放比例设成适合一页宽:
worksheet.PageSetup.Zoom = false; worksheet.PageSetup.FitToPagesWide = 1; worksheet.PageSetup.FitToPagesTall = false;3.3 常见格式丢失场景及恢复方式
没有任何方案是完美的,这个方法再省事,也有几个格式盲区需要你自己补。
页眉页脚是丢失重灾区。Word文档里的页眉、页脚、页码这些信息,转成HTML之后根本不存在,因为HTML本身就没有页眉页脚的概念。Excel打开后自然也不会自动生成。如果转换的文件需要保留页眉信息,我一般是在转换完成后用Excel的PageSetup属性手动写上。
另外一个常见问题是文本框内容。Word里的文本框,转成HTML后会变成带绝对定位的div样式,Excel虽然能打开,但文本框的位置经常跑偏,或者被压到表格下方看不见。处理这类文档,目前没有太优雅的自动方案,我通常的做法是转换后人工检查一遍,或者让业务方在原始Word里尽量少用文本框排版。
| 格式要素 | HTML中间方案表现 | 恢复/处理方式 |
|---|---|---|
| 表格边框、底色、字体颜色 | 完整保留 | 无需处理 |
| 单元格合并 | 完整保留 | 无需处理 |
| 图片 | 保留但相对路径引用 | 另存xlsx后嵌入,需保留临时文件夹直到另存完成 |
| 页眉页脚、页码 | 丢失 | 转换后通过Excel PageSetup手动补齐 |
| 文本框 | 位置可能偏移 | 转换后人工修正 |
| SmartArt、图表 | 可能变成图片或丢失 | 建议用Aspose/Spire处理 |
| 多级列表编号 | 基本保留 | 个别情况需在Excel里手动调整缩进 |
4. 完整可运行的示例与多文档批量处理
4.1 单文件转换完整代码
前面给的代码是核心逻辑,但真要在生产环境跑,还需要一个带日志和控制台的完整入口。下面的代码把转换过程加上计时和状态输出,方便放到内网工具里直接打包发布:
using System; using System.Diagnostics; using System.IO; class Program { static void Main(string[] args) { if (args.Length < 2) { Console.WriteLine("用法: WordToExcel.exe <Word文件路径> <Excel输出路径>"); return; } string input = Path.GetFullPath(args[0]); string output = Path.GetFullPath(args[1]); if (!File.Exists(input)) { Console.WriteLine($"错误: 输入文件不存在 - {input}"); return; } string ext = Path.GetExtension(input).ToLower(); if (ext != ".doc" && ext != ".docx") { Console.WriteLine("错误: 仅支持 .doc 和 .docx 文件"); return; } string outputDir = Path.GetDirectoryName(output); if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } Stopwatch sw = Stopwatch.StartNew(); try { Console.WriteLine($"正在转换: {Path.GetFileName(input)}"); WordToExcelConverter.Convert(input, output); sw.Stop(); Console.WriteLine($"转换完成,耗时 {sw.Elapsed.TotalSeconds:F2} 秒"); Console.WriteLine($"输出文件: {output}"); } catch (Exception ex) { sw.Stop(); Console.WriteLine($"转换失败: {ex.Message}"); Console.WriteLine(ex.StackTrace); Environment.ExitCode = 1; } } }这里加了几层防护:检查输入文件存在、检查扩展名、自动创建输出目录。别小看这些,我在部署工具时发现,很多人喜欢把Word文件放在桌面或者下载目录,路径里带中文、带空格都很常见,Path.GetFullPath能提前把这种路径解析好,避免Office打开时因为路径问题抛COM异常。
4.2 批量转换文件夹内所有Word文件
单个文件转换搞定后,批量就很自然了。批量场景下有个重要经验:千万不要一个文件新建一个Word.Application实例,要复用同一个实例。Word.Application的启动非常重,动辄好几秒,批量转换50个文件,频繁启停光等待时间就够喝一壶的。
下面是批量转换的核心写法:
public static void ConvertBatch(string inputDir, string outputDir, string searchPattern = "*.doc*") { if (!Directory.Exists(inputDir)) throw new DirectoryNotFoundException($"输入目录不存在: {inputDir}"); Directory.CreateDirectory(outputDir); string[] files = Directory.GetFiles(inputDir, searchPattern); Console.WriteLine($"找到 {files.Length} 个Word文件,开始转换..."); Word.Application wordApp = null; Excel.Application excelApp = null; try { wordApp = new Word.Application(); wordApp.Visible = false; wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone; excelApp = new Excel.Application(); excelApp.Visible = false; excelApp.DisplayAlerts = false; excelApp.ScreenUpdating = false; int successCount = 0; foreach (string file in files) { string fileName = Path.GetFileNameWithoutExtension(file); string outputFile = Path.Combine(outputDir, fileName + ".xlsx"); if (File.Exists(outputFile)) { Console.WriteLine($"跳过(已存在): {fileName}"); continue; } try { ConvertWithExistingApps(wordApp, excelApp, file, outputFile); successCount++; Console.WriteLine($"[{successCount}/{files.Length}] 完成: {fileName}"); } catch (Exception ex) { Console.WriteLine($"[失败] {fileName}: {ex.Message}"); } } Console.WriteLine($"批量转换结束,成功 {successCount} 个,失败 {files.Length - successCount} 个。"); } finally { if (wordApp != null) { wordApp.Quit(); Marshal.FinalReleaseComObject(wordApp); } if (excelApp != null) { excelApp.Quit(); Marshal.FinalReleaseComObject(excelApp); } } }ConvertWithExistingApps这个方法就是把单文件转换里的Word和Excel创建部分去掉,直接传入公共实例。注意别在多线程里跑这个逻辑。Word.Application和Excel.Application的COM对象都是单线程模型,Parallel循环同时调用会引发莫名其妙的崩溃和死锁,我当时试过用Task并行处理20个文件,跑一半就挂了,老老实实改回同步循环后一切正常。
4.3 不依赖Office的备选方案
有些场景确实没办法装Office,比如纯Linux服务器环境。这种情况下如果还想保留格式,可以走Spire.Doc加Spire.XLS的免费路线,或者直接上Aspose全家桶。这里给一段Spire的参考代码,它不需要Office就能把Word表格读出来写进Excel,但不建议在格式要求高的场景用它:
using Spire.Doc; using Spire.Doc.Documents; using Spire.Xls; // 加载Word文档 Document doc = new Document(); doc.LoadFromFile("input.docx"); // 创建Excel工作簿 Workbook workbook = new Workbook(); Worksheet sheet = workbook.Worksheets[0]; int rowIndex = 1; foreach (Section section in doc.Sections) { foreach (DocumentObject obj in section.Body.ChildObjects) { if (obj is Table table) { for (int i = 0; i < table.Rows.Count; i++) { for (int j = 0; j < table.Rows[i].Cells.Count; j++) { string text = table.Rows[i].Cells[j].GetText(); sheet.Range[rowIndex, j + 1].Text = text.Trim(); } rowIndex++; } } } } workbook.SaveToFile("output.xlsx", ExcelVersion.Version2016);这段代码的问题很明显:只提取了文本内容,单元格合并、边框、字体样式全丢了。Spire免费版有文档段落数和表格数量的限制,文件一复杂就跑不动。Aspose.Words加Aspose.Cells的效果会好很多,特别是Aspose.Words转HTML再让Aspose.Cells打开那条路,跟本文思路异曲同工,但商业授权确实贵。所以我的结论是:能用Office环境就跑COM方案,实在受限于环境再考虑商业组件。
5. 常见问题与排查技巧实录
5.1 表格行列错乱、合并单元格丢失
这个问题的排查思路很重要:先分清是哪一步出的问题。把中间的HTML文件用浏览器打开看一眼,如果浏览器里表格本身已经错乱,说明问题出在Word另存HTML那一步,大概率是文档里有嵌套表格或者跨页的大表格;如果浏览器里正常但Excel打开后错乱,就要检查Excel打开时的解析逻辑。
Word里嵌套表格是最坑的,一个表格里再嵌一个小表格,转成HTML后层级复杂,Excel打开时经常把小表格挤到奇怪的位置。遇到这类文档,我目前没有特别完美的自动修复方案,实际处理是对异常文件单独编译一个清洗流程。如果只是个别文件需要处理,直接人工在Excel里拖一下位置比写代码修复更快。
5.2 转换后数字变文本、日期格式不对
这是HTML中间格式方案最常见的一个后续问题。Word表格里的数字在HTML里就是纯文本,Excel打开时默认当成文本处理,单元格左上角会出现绿色小三角,无法直接参与求和、排序。日期同理,2024-01-15这种格式在Excel里可能变成一串数字或者仍是文本。
解决办法是转换完成后做一次数据修正。用C#或者录制宏都行,我常用的是对UsedRange做一次列类型判断和转换:
// 修正数字列 Excel.Range usedRange = worksheet.UsedRange; usedRange.NumberFormat = "General"; // 把文本型数字转成数值 for (int col = 1; col <= usedRange.Columns.Count; col++) { Excel.Range columnRange = usedRange.Columns[col]; columnRange.TextToColumns( Destination: columnRange, DataType: Excel.XlTextParsingType.xlDelimited, TextQualifier: Excel.XlTextQualifier.xlTextQualifierNone); }TextToColumns是Excel内置的“分列”功能,它能触发Excel对文本数字的自动识别,把文本型数字转成真正的数值。这个方法在处理从HTML导入的数据时非常好用,而且不会破坏原有格式。
5.3 进程残留与Word关闭慢
这个场景我放在最后但最值得关注。很多人用这个方案后反馈电脑变卡了、Word关闭特别慢,十有八九是COM对象没有被完全释放。
进程残留的排查步骤很简单:任务管理器里数一下有几个WINWORD.EXE和EXCEL.EXE。正常状态应该是一个都没有,如果残留好几个,说明代码里某些分支没有执行Quit或FinalReleaseComObject。特别是catch分支里,如果转换中途抛异常,后面的Quit代码没跑到,进程就挂住了。
可靠的做法是在finally块强制清理,同时只在确认是本次启动的进程时才去杀进程。参考实现:
static void KillProcessStartedByUs(Process beforeWord, Process beforeExcel) { foreach (Process p in Process.GetProcessesByName("WINWORD")) { if (p.StartTime > beforeWord.StartTime) p.Kill(); } foreach (Process p in Process.GetProcessesByName("EXCEL")) { if (p.StartTime > beforeExcel.StartTime) p.Kill(); } }这个方案只杀转换期间新启动的Office进程,保障用户自己打开的文档不会受牵连。但Process.StartTime偶尔会报异常,需要自己包一层try-catch。另外杀进程属于最后的兜底手段,日常还是要靠代码正确释放,不能把兜底当主策略。
5.4 性能优化经验
最后补充一批实际跑批时用过的性能优化手段,这些都是常规文档里不会写的经验。
关闭一切不必要的界面刷新。Word.Application和Excel.Application启动后,马上把Visible设为false、ScreenUpdating设为false、DisplayAlerts设为false。这三个设置能省掉大部分等待时间,尤其Excel打开大HTML时,每刷新一次屏幕都要消耗不少时间。
批量处理时复用Application实例,不要每个文件都new一个。我测过,复用单个Word实例连续处理50个文件,平均每个2秒左右;如果每个文件新建实例,前面2秒基本都耗在启动进程上。
大文件拆批处理。单个Word超过50页的文档,转成HTML后体积很大,Excel打开可能要半分钟。如果需求允许,建议在原始文档层面就先拆成多个小文件再转换,整体效率反而更高。另外转换大文件期间别动电脑,Office的COM组件在极端情况下会弹出“无响应”对话框,虽然没有真正死掉,但会阻塞调用,需要手动点击或等它恢复,自动化流程里碰到这种场景最闹心。
5.5 转换结果与原始文档的比对方法
不管方案多成熟,上线前一定要做一轮比对测试。我的做法是:挑5类典型文档,包括纯表格型、图文混排型、带页眉页脚型、带图片型、复杂合并单元格型,用工具转换后用Excel打开,跟Word原文件并排对照检查,重点看三点:表格线是否连续、合并单元格是否一一对应、图片是否在同一位置。
比对过程里配合使用“打印预览”模式检查分页情况,因为Excel的屏幕显示和实际打印效果经常有偏差。发现问题优先调整工具侧的参数设置,实在调不了的再人工修正。
这套方案我从零到上线用了不到一个工作日,后续支撑了上千次文档转换,稳定性和格式保留程度都被实践验证过。老实说,它不是我写过最“高级”的代码,但绝对是我交付效率最高、问题最少的一个办公自动化工具。如果你也只是想把一批Word表格转成Excel,并且不想研究那些复杂的数据结构解析,这个三步方案就是个可以直接抄作业的最优解。最后分享一个自己在实际使用中的小技巧:转换前尽量确认Word文档里没有使用文本框排版,字体尽量统一成宋体或微软雅黑,这几个看似无关紧要的细节,对转换后的排版稳定性影响比想象中大得多。