当前位置:首页 > 技术 > 正文内容

基于OpenXML SDK的Word表格数据提取与清洗实践

访客 技术 2026年9月1日 1

技术背景与架构选型

在企业级数据采集中,常需直接读取 Word 文档内的结构化信息(如表格行列及单元格文本),同时要求规避本地 Office 套件依赖以消除授权限制与后台进程残留问题。采用微软官方提供的 DocumentFormat.OpenXml.dll 配合 WindowsBase.dll 是一种高效的轻量级方案。该方案通过直接解析 .docx 底层的 XML 规范实现内容抽取,支持高度自定义的解析规则。需注意,该机制仅兼容 Office Open XML 标准格式,无法直接读取旧版 .doc 文件。此外,WPS、LibreOffice 等第三方办公软件生成的 XML 节点结构可能存在细微差异,实际工程中需通过容错逻辑进行兼容适配。

Word文档表格结构示意

核心解析流程与实现

数据提取的核心在于定位文档主体(Body),逐级向下遍历表格(Table)、行(TableRow)与单元格(TableCell)节点。由于 OpenXML 对象模型未为单元格集合提供基于索引的访问器,通常需依赖迭代器配合原始 XML 字符串匹配来定位关键数据。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

public class DocxTableParser
{
    public static IEnumerable<List<string>> ExtractTableContent(Stream docStream)
    {
        using var wordPackage = WordprocessingDocument.Open(docStream, false);
        var documentBody = wordPackage.MainDocumentPart.Document.Body;

        // 获取文档内所有表格节点
        var tableNodes = documentBody.Descendants<Table>();
        
        foreach (var currentTable in tableNodes)
        {
            var extractedRows = new List<string>();
            foreach (var rowElement in currentTable.Descendants<TableRow>())
            {
                var cellValues = new List<string>();
                foreach (var cellElement in rowElement.Descendants<TableCell>())
                {
                    var rawCellMarkup = cellElement.InnerXml;
                    // 检测是否包含列表或段落样式标记
                    bool hasFormatTags = rawCellMarkup.Contains("<w:numPr>") || rawCellMarkup.Contains("<w:pPr>");
                    
                    string cellText;
                    if (hasFormatTags)
                    {
                        cellText = TextSanitizer.RemoveXmlFormatting(rawCellMarkup);
                    }
                    else
                    {
                        cellText = rowElement.InnerText.Trim();
                    }
                    
                    if (!string.IsNullOrWhiteSpace(cellText))
                    {
                        cellValues.Add(cellText);
                    }
                }
                extractedRows.AddRange(cellValues);
            }
            yield return extractedRows;
        }
    }
}

XML 标记剥离与文本清洗

提取的原始内容通常混杂大量样式定义、空白控制符及排版标签。通过编译型正则表达式可高效过滤无效节点,并处理常见的 HTML/XML 实体转义及特殊排版字符(如复选框符号 □)。

public static class TextSanitizer
{
    private static readonly Regex TagPattern = new Regex(@"<[^>]+>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    private static readonly Regex EntityPattern = new Regex(@"&\w+;|&#\d+;", RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public static string RemoveXmlFormatting(string rawInput)
    {
        if (string.IsNullOrEmpty(rawInput)) return string.Empty;
        
        var cleaned = TagPattern.Replace(rawInput, string.Empty);
        cleaned = EntityPattern.Replace(cleaned, string.Empty);
        
        // 标准化空白字符与换行
        cleaned = Regex.Replace(cleaned, @"\s+", " ");
        
        // 移除特定厂商生成的排版占位符 (例如 U+25A1 方块符号)
        cleaned = cleaned.Replace("\u25A1", string.Empty);
        
        return cleaned.Trim();
    }
}

前端富文本粘贴预处理策略

当业务场景涉及用户将 Word 内容直接粘贴至网页编辑器并捕获 innerHTML 时,建议在前端执行初步格式化。通过统一换行标识、剔除冗余样式块及折叠连续空行,可显著降低后端二次解析的复杂度。

function preprocessPastedContent(rawHtml) {
  let processed = rawHtml;
  const newlineToken = "@rn";

  // 将常见换行元素替换为统一标识
  processed = processed.replace(/<br\s*\/?>|<\/p>/gi, newlineToken);
  processed = processed.replace(/<span[^>]*style="[^"]*white-space:pre[^"]*"[^>]*>/gi, newlineToken);

  // 批量移除 HTML/XML 标签结构
  processed = processed.replace(/<[^>]+>/g, '');

  // 清理特殊占位符与非常规空白
  const charFilterPattern = /\u25A1|\u00A0|\t/g;
  processed = processed.replace(charFilterPattern, '');

  // 合并连续换行符
  const multiLinePattern = new RegExp(`(${newlineToken})\\s*\\1+`, 'g');
  processed = processed.replace(multiLinePattern, newlineToken);

  return processed.trim();
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。