C# Winform使用NPOI获取Word内容:零基础也能精通的实战指南!

为什么选择NPOI?

NPOI核心优势

  1. 无需Office依赖:纯.NET库解析.doc/.docx
  2. 支持复杂结构:表格、段落、图片、样式全解析
  3. 高性能低内存:比COM快5-10倍,内存占用降低70%

模块1:环境准备与项目搭建

1.1 安装NPOI库:告别“Hello World”陷阱

“NuGet安装后找不到类?90%的初学者都踩过这个坑!”

// Visual Studio NuGet命令行  
Install-Package NPOI -Version 2.5.4  
Install-Package NPOI.XWPF.UserModel -Version 2.5.4  
Install-Package NPOI.HWPF -Version 2.5.4  

关键点

  • HWPF:处理.doc(旧版Word)
  • XWPF:处理.docx(新版Word)
  • 版本匹配:确保项目.NET Framework ≥ 4.6

1.2 创建Winform窗体:从空白到功能按钮

“按钮点击事件写错命名空间?看这里!”

// Form1.cs  
public partial class Form1 : Form  
{  
    public Form1()  
    {  
        InitializeComponent();  
        // 初始化按钮  
        Button btnParse = new Button();  
        btnParse.Text = "解析Word";  
        btnParse.Location = new Point(50, 50);  
        btnParse.Click += BtnParse_Click;  
        this.Controls.Add(btnParse);  
    }  

    private void BtnParse_Click(object sender, EventArgs e)  
    {  
        try  
        {  
            OpenFileDialog openFileDialog = new OpenFileDialog();  
            openFileDialog.Filter = "Word文档|*.doc;*.docx";  
            if (openFileDialog.ShowDialog() == DialogResult.OK)  
            {  
                string filePath = openFileDialog.FileName;  
                ParseWordDocument(filePath);  
            }  
        }  
        catch (Exception ex)  
        {  
            MessageBox.Show($"解析失败:{ex.Message}");  
        }  
    }  
}

调试技巧

  • 异常捕获:务必包裹try-catch,防止程序崩溃
  • 文件过滤:通过Filter属性限定文件类型

模块2:核心功能实现——段落与表格解析

2.1 读取.docx文件:XWPFDocument的魔法

“段落遍历卡在空行?你需要知道RowNum的隐藏规则!”

private void ParseWordDocument(string filePath)  
{  
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
    {  
        IWorkbook workbook = null;  
        if (filePath.EndsWith(".docx"))  
        {  
            workbook = new XSSFWorkbook(fs);  
        }  
        else if (filePath.EndsWith(".doc"))  
        {  
            workbook = new HSSFWorkbook(fs);  
        }  

        if (workbook != null)  
        {  
            foreach (ISheet sheet in workbook)  
            {  
                foreach (IRow row in sheet)  
                {  
                    foreach (ICell cell in row)  
                    {  
                        Console.WriteLine($"单元格内容:{cell.ToString()}");  
                    }  
                }  
            }  
        }  
    }  
}

进阶优化

  • 类型判断:通过cell.CellType处理数字/日期
  • 样式保留cell.GetCellStyle()获取字体、颜色

2.2 提取表格数据:嵌套循环的“陷阱”与“解药”

“表格行数少1?RowNum从0开始计数的真相!”

private void ExtractTableData(XWPFDocument doc)  
{  
    foreach (XWPFTable table in doc.Tables)  
    {  
        Console.WriteLine("发现表格:");  
        for (int i = 0; i < table.Rows.Count; i++)  
        {  
            XWPFTableRow row = table.Rows[i];  
            for (int j = 0; j < row.Cells.Count; j++)  
            {  
                XWPFTableCell cell = row.Cells[j];  
                // 处理单元格中的段落  
                string cellText = string.Join("\n", cell.Paragraphs.Select(p => p.Text));  
                Console.WriteLine($"行{i}{j}{cellText}");  
            }  
        }  
    }  
}

关键技巧

  • 段落合并string.Join处理多段落单元格
  • 表格定位:通过table.GetCTTbl().GetPos()获取位置信息

模块3:高级功能——图片与样式提取

3.1 提取图片:Base64编码的“隐藏通道”

“图片保存路径错误?你需要绝对路径+文件名哈希!”

private void ExtractImages(XWPFDocument doc, string outputDir)  
{  
    if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);  

    int imageIndex = 0;  
    foreach (XWPFPictureData picture in doc.GetAllPictures())  
    {  
        string ext = GetImageExtension(picture.PictureType);  
        string imagePath = Path.Combine(outputDir, $"image_{imageIndex++}{ext}");  

        using (FileStream fs = new FileStream(imagePath, FileMode.Create))  
        {  
            picture.WriteImageContent(fs);  
            Console.WriteLine($"图片已保存:{imagePath}");  
        }  
    }  
}  

private string GetImageExtension(int pictureType)  
{  
    switch (pictureType)  
    {  
        case 2: return ".jpg";  
        case 3: return ".png";  
        case 4: return ".gif";  
        default: return ".bin";  
    }  
}

注意事项

  • 格式识别PictureType可能返回未知类型
  • 内存释放using语句确保流正确关闭

3.2 保留样式:字体、颜色的“克隆术”

“样式丢失?你需要深入ICellStyle的每个属性!”

private void PreserveStyles(ICell cell)  
{  
    ICellStyle style = cell.GetCellStyle();  
    Console.WriteLine($"字体:{style.Font.FontName}");  
    Console.WriteLine($"字号:{style.Font.FontSize}");  
    Console.WriteLine($"颜色:{style.FillForegroundColor}");  
    Console.WriteLine($"加粗:{style.Font.IsBold}");  
}

扩展应用

  • 样式映射:将Excel样式转换为HTML/CSS
  • 条件格式:根据背景色标记重要数据

模块4:实战案例——从Word到数据库的完整链路

4.1 数据映射实体类:字段命名的艺术

“数据库字段命名混乱?用[Column]特性统一规范!”

[Table("WordData")]  
public class WordDataEntity  
{  
    [Key]  
    public int Id { get; set; }  

    [Column("OriginalText")]  
    public string Content { get; set; }  

    [Column("ExtractTime")]  
    public DateTime ExtractedAt { get; set; }  
}

EF Core配置

  • 自动迁移Add-Migration InitialCreate
  • 批量插入SaveChanges() vs BulkInsert

4.2 数据库写入:异步操作的“性能炸弹”

“同步阻塞导致UI卡顿?异步编程是关键!”

private async Task SaveToDatabaseAsync(List<WordDataEntity> data)  
{  
    using (var context = new WordDataContext())  
    {  
        context.WordData.AddRange(data);  
        await context.SaveChangesAsync();  
    }  
}

性能优化

  • 批量提交:每100条数据提交一次
  • 连接池配置Pooling=true;Max Pool Size=200;

模块5:异常处理与性能调优

5.1 常见错误与解决方案

“文件损坏导致解析失败?你需要健壮的异常处理!”

private void SafeParse(string filePath)  
{  
    try  
    {  
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
        {  
            if (filePath.EndsWith(".docx"))  
            {  
                var doc = new XWPFDocument(fs);  
                // 解析逻辑  
            }  
            else if (filePath.EndsWith(".doc"))  
            {  
                var doc = new HWPFDocument(fs);  
                // 解析逻辑  
            }  
        }  
    }  
    catch (IOException ex)  
    {  
        Console.WriteLine($"IO异常:{ex.Message}");  
    }  
    catch (InvalidDataException ex)  
    {  
        Console.WriteLine($"文件格式错误:{ex.Message}");  
    }  
}

日志记录

  • SerilogLog.Information("解析开始")
  • 文件分级:按日期生成日志文件

5.2 内存优化:大型文档的“瘦身术”

“500MB文档导致内存爆表?流式处理是关键!”

private void StreamParse(string filePath)  
{  
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
    {  
        if (filePath.EndsWith(".docx"))  
        {  
            using (var doc = new XWPFDocument(fs))  
            {  
                foreach (var para in doc.Paragraphs)  
                {  
                    ProcessParagraph(para);  
                }  
            }  
        }  
    }  
}

关键策略

  • 逐行处理:避免一次性加载全部内容
  • 对象池:复用StringBuilder等对象

从入门到精通的“跃迁之路”

“掌握NPOI后,你的生产力直接提升10倍!”

  1. 核心思想

    • 分层架构:UI层、业务层、数据层分离
    • 异常防御:全面覆盖所有可能错误场景
    • 性能优先:异步处理+流式解析
  2. 进阶方向

    • OCR集成:处理扫描版PDF转Word
    • RPA自动化:结合UIPath实现端到端流程
    • 微服务化:将解析功能封装为REST API
  3. 终极目标

    • 构建企业级文档处理平台:支持Word/Excel/PDF多格式互转

你可能感兴趣的:(C#学习资料,c#,word,开发语言)