You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Open XML SDK 2.0与C#按指定文本检索Word章节及段落并入库?

解决C# Open XML SDK检索Word章节及段落并插入数据库的问题

原代码的核心问题分析

先帮你拆解下当前代码返回空值的原因:

  • 正则表达式使用错误:你用p.InnerText.Contains("Chapter [0-9]{1;} ")来匹配章节,但Contains字面字符串匹配,根本不支持正则语法,而且正则里的量词应该是{1,}(匹配1个或多个数字),不是{1;},这两个问题导致完全匹配不到章节标题。
  • 缺少内容关联逻辑:就算匹配到章节,你也只提取了标题,没把后续的子标题和段落内容关联起来,更没有处理数据库插入的逻辑。

完整实现方案

下面是修正后的代码,包含章节匹配、内容关联、数据库插入的完整流程,完全适配你的需求:

1. 修正后的核心代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Text.RegularExpressions;
using System.Data.SqlClient; // 需添加数据库访问引用

public partial class Default : Page
{
    // 替换成你的数据库连接字符串
    private string _dbConnString = "Data Source=你的服务器地址;Initial Catalog=你的数据库名;Integrated Security=True;";

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            string wordFilePath = @"C:\Users\file.docx";
            // 提取指定章节的内容(这里指定"Chapter 1 - Events")
            var targetChapterData = ExtractTargetChapterData(wordFilePath, "Chapter 1 - Events");
            
            if (targetChapterData != null && targetChapterData.SubheadingContents.Any())
            {
                InsertChapterDataToDb(targetChapterData);
                Response.Write("指定章节内容已成功插入数据库!");
            }
            else
            {
                Response.Write("未找到匹配的章节内容。");
            }
        }
    }

    // 提取指定章节的子标题和对应段落
    private ChapterData ExtractTargetChapterData(string filePath, string targetChapterTitle)
    {
        ChapterData targetChapter = null;
        ChapterData currentChapter = null;
        // 匹配章节标题:兼容短横线和长破折号,忽略大小写
        var chapterRegex = new Regex(@"Chapter \d+ [–-] .+", RegexOptions.IgnoreCase);
        // 匹配子标题:以"- "开头的行
        var subheadingRegex = new Regex(@"^- .+", RegexOptions.Multiline);

        // 只读打开文档,避免不必要的锁
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filePath, false))
        {
            Body docBody = wordDoc.MainDocumentPart.Document.Body;
            foreach (var para in docBody.Descendants<Paragraph>())
            {
                string paraText = para.InnerText.Trim();
                if (string.IsNullOrEmpty(paraText)) continue;

                // 匹配到章节标题
                if (chapterRegex.IsMatch(paraText))
                {
                    // 先把之前的章节收尾
                    if (currentChapter != null)
                    {
                        FinalizeCurrentSubheading(currentChapter);
                    }
                    // 判断是否是目标章节
                    if (paraText.Equals(targetChapterTitle, StringComparison.OrdinalIgnoreCase))
                    {
                        currentChapter = new ChapterData { ChapterTitle = paraText };
                        targetChapter = currentChapter;
                    }
                    else
                    {
                        // 不是目标章节,跳过后续内容直到下一个章节
                        currentChapter = null;
                    }
                }
                // 当前处于目标章节内,匹配子标题
                else if (currentChapter != null && subheadingRegex.IsMatch(paraText))
                {
                    FinalizeCurrentSubheading(currentChapter);
                    // 去除开头的"- ",提取子标题文本
                    currentChapter.CurrentSubheading = paraText.TrimStart('-', ' ');
                    currentChapter.CurrentContent = "";
                }
                // 当前处于目标章节的子标题下,收集段落内容
                else if (currentChapter != null && !string.IsNullOrEmpty(currentChapter.CurrentSubheading))
                {
                    currentChapter.CurrentContent += paraText + " ";
                }
            }
            // 处理最后一个子标题的内容
            if (currentChapter != null)
            {
                FinalizeCurrentSubheading(currentChapter);
            }
        }
        return targetChapter;
    }

    // 辅助方法:把当前子标题和内容加入章节列表
    private void FinalizeCurrentSubheading(ChapterData chapter)
    {
        if (!string.IsNullOrEmpty(chapter.CurrentSubheading))
        {
            chapter.SubheadingContents.Add(new SubheadingContent
            {
                Subheading = chapter.CurrentSubheading,
                Content = chapter.CurrentContent.Trim()
            });
            chapter.CurrentSubheading = null;
            chapter.CurrentContent = "";
        }
    }

    // 将章节数据插入数据库
    private void InsertChapterDataToDb(ChapterData chapterData)
    {
        using (SqlConnection conn = new SqlConnection(_dbConnString))
        {
            conn.Open();
            // 参数化查询避免SQL注入
            string insertSql = @"INSERT INTO chapters (chapter, subheading, contents) 
                                 VALUES (@ChapterTitle, @Subheading, @Content)";
            
            foreach (var subContent in chapterData.SubheadingContents)
            {
                using (SqlCommand cmd = new SqlCommand(insertSql, conn))
                {
                    cmd.Parameters.AddWithValue("@ChapterTitle", chapterData.ChapterTitle);
                    cmd.Parameters.AddWithValue("@Subheading", subContent.Subheading);
                    cmd.Parameters.AddWithValue("@Content", subContent.Content);
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }

    // 辅助类:存储章节的结构化数据
    private class ChapterData
    {
        public string ChapterTitle { get; set; }
        public string CurrentSubheading { get; set; }
        public string CurrentContent { get; set; } = "";
        public List<SubheadingContent> SubheadingContents { get; set; } = new List<SubheadingContent>();
    }

    // 辅助类:存储子标题和对应内容
    private class SubheadingContent
    {
        public string Subheading { get; set; }
        public string Content { get; set; }
    }
}

2. 关键改进说明

  • 正则匹配修复:用Regex.IsMatch替代Contains实现正则匹配,修正了量词错误,同时兼容文档中的短横线和长破折号。
  • 结构化内容提取:通过跟踪当前章节、当前子标题,把段落内容正确关联到对应的层级下,确保数据结构和文档一致。
  • 数据库安全插入:使用参数化查询避免SQL注入,严格按照你提供的chapters表结构插入数据。
  • 目标章节筛选:可以直接指定要检索的章节标题,只提取目标内容,不需要处理无关章节。

3. 注意事项

  • 确保项目已安装DocumentFormat.OpenXml NuGet包(右键项目→管理NuGet程序包→搜索安装)。
  • 替换代码中的数据库连接字符串为你自己的服务器和数据库信息。
  • 如果文档中章节标题的格式有变化,可以调整chapterRegex的正则表达式来适配。

测试效果

针对你提供的示例文档,运行代码后,Chapter 1 - Events的内容会被插入到数据库中,每条记录对应一个子标题和其段落内容,比如:

chaptersubheadingcontents
Chapter 1 - Eventsalert or disservicesLorem ipsum dolor sit amet, consectetur adipiscing elit …. ….
Chapter 1 - Eventssignificant activitiesPhasellus dui nunc, rutrum vitae dictum eleifend, ullamcorper hendrerit sem …. ….

内容的提问来源于stack exchange,提问作者Chevy Mark Sunderland

火山引擎 最新活动