使用 C# 高效解析 PDF 文档:文本与表格提取实战指南

白榆
白榆
发布时间:2026-08-23| 146人浏览过
原创

前言

PDF 在合同、报告、说明书和业务归档中非常常见,但一旦需要批量检索、抽取字段或整理表格,手工复制几乎不可持续。对 C# 开发者来说,关键不只是“能不能读出 PDF”,而是如何在保留代码可维护性的前提下,稳定完成文本与表格提取。本文按“环境准备、文本提取、表格识别、工程化注意事项”四个层次展开,并结合 Spire.PDF for .NET 的示例代码,帮助你判断它适合处理哪些任务、又该如何规避常见问题。

PDF 在合同、报告、说明书和业务归档中非常常见,但一旦需要批量检索、抽取字段或整理表格,手工复制几乎不可持续。对 C# 开发者来说,关键不只是“能不能读出 PDF”,而是如何在保留代码可维护性的前提下,稳定完成文本与表格提取。本文按“环境准备、文本提取、表格识别、工程化注意事项”四个层次展开,并结合 Spire.PDF for .NET 的示例代码,帮助你判断它适合处理哪些任务、又该如何规避常见问题。

先把环境搭起来:安装库并验证 PDF 能否正常加载

PDF 不是普通的纯文本文件,它本质上是一种页面描述格式,内部可能包含文本、字体、图像、矢量对象以及复杂的布局信息。因此,实际开发中通常不会直接去解析底层二进制结构,而是通过成熟的 PDF 处理库来完成读取和提取。

本文示例选用 Spire.PDF for .NET。它提供了创建、编辑、转换和解析 PDF 的 API,适合在 C# 项目中快速落地常见文档处理需求。

NuGet 安装命令

在 Visual Studio 项目中,可通过 NuGet 包管理器安装:

Install-Package Spire.PDF

安装完成后,就可以在代码中引用相关命名空间并开始处理 PDF 文件。

先做最小验证:加载文档并读取页数

无论后续要提取文本还是识别表格,第一步都应该先确认文件能够被正常加载。下面这段代码演示了如何打开 PDF 并获取总页数:

using Spire.Pdf;
using System;

namespace PdfParserTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            // 假设您的PDF文件位于项目根目录下的"Data"文件夹中
            string pdfFilePath = "Data/Sample.pdf";

            // 创建一个PdfDocument实例
            PdfDocument doc = new PdfDocument();

            try
            {
                // 从文件加载PDF文档
                doc.LoadFromFile(pdfFilePath);

                // 获取并打印PDF文档的总页数
                Console.WriteLine($"PDF文档 '{pdfFilePath}' 已成功加载。");
                Console.WriteLine($"总页数: {doc.Pages.Count}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"加载PDF文档时发生错误: {ex.Message}");
            }
            finally
            {
                // 释放资源
                doc.Close();
            }

            Console.ReadKey();
        }
    }
}

这里涉及几个最基础、但也是后续高频使用的对象和方法:

  • PdfDocument 是操作 PDF 的核心类。
  • LoadFromFile() 用于从指定路径加载文件。
  • doc.Pages.Count 可以获取文档总页数。
  • doc.Close() 用于释放文件句柄和相关资源,避免内存占用持续增长。

文本怎么提:整页提取与按区域提取各适合什么场景

在 PDF 自动化处理中,文本提取通常是最常见的需求。比如做关键字检索、报告归档、字段抽取或数据迁移时,首先都要把页面里的文字稳定拿出来。

展示 C# 使用 Spire.PDF 处理整页文本与区域文本的两种路径对比信息图
PDF 文本提取两种路径用一张图区分整页文本提取与区域文本提取的适用场景、关键对象和输出结果。

不过,PDF 文本并不总是线性排列的。它可能被分散在不同对象层里,也可能受到字体嵌入、编码方式或布局结构的影响。因此,实际处理时通常要区分两种思路:一种是提取整页文本,另一种是针对特定区域做定向抽取。

提取所有页面的纯文本

如果目标是把整份 PDF 转成可搜索、可保存的文本文件,可以直接遍历每一页并逐页提取:

using Spire.Pdf;
using Spire.Pdf.Texts;
using System;
using System.IO;
using System.Text;

namespace PdfParserTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            string pdfFilePath = "Data/Sample.pdf";
            PdfDocument doc = new PdfDocument();
            doc.LoadFromFile(pdfFilePath);

            StringBuilder allText = new StringBuilder();

            // 遍历文档中的每一页
            for (int i = 0; i < doc.Pages.Count; i++)
            {
                PdfPageBase page = doc.Pages[i];

                // 创建PdfTextExtractor实例
                PdfTextExtractor extractor = new PdfTextExtractor(page);
                
                // 设置提取选项,例如是否保留空格
                PdfTextExtractOptions options = new PdfTextExtractOptions();
                options.IsExtractAllText = true; // 设置为true可以提取所有文本,包括隐藏文本

                // 提取当前页的文本内容
                string pageText = extractor.ExtractText(options);
                allText.AppendLine($"--- 第 {i + 1} 页文本内容 ---");
                allText.AppendLine(pageText);
                allText.AppendLine();
            }

            // 将所有文本内容写入文件或打印到控制台
            File.WriteAllText("ExtractedText.txt", allText.ToString());
            Console.WriteLine("所有文本内容已提取并保存到 ExtractedText.txt");

            doc.Close();
            Console.ReadKey();
        }
    }
}

这段代码的适用点很明确:

  • 用 PdfTextExtractor 从指定页面提取文本。
  • 通过 PdfTextExtractOptions 控制提取行为。
  • IsExtractAllText = true 可尝试提取更多文本内容,包括可能被遮挡或处于特殊层级的文字。

如果你的目标是做全文检索、建立文本索引或保存为审计记录,这种“全页扫描”的方式通常最直接。

只提取指定区域文本,适合表单和固定版式文档

有些业务并不关心整页内容,只想拿到固定位置的一块文字。比如发票上的金额区域、表单中的姓名字段,或者报告首页的摘要框。这时,按矩形区域提取会更高效,也更容易减少无关文本干扰。

using Spire.Pdf;
using Spire.Pdf.Texts;
using System;
using System.Drawing; // 引入System.Drawing命名空间处理RectangleF

namespace PdfParserTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            string pdfFilePath = "Data/Sample.pdf";
            PdfDocument doc = new PdfDocument();
            doc.LoadFromFile(pdfFilePath);

            // 获取第一页
            PdfPageBase page = doc.Pages[0];

            // 定义一个要提取文本的矩形区域 (x, y, width, height)
            // 这里的坐标和尺寸单位通常是PDF点(1/72英寸),原点在左下角或左上角,具体取决于PDF的设置。
            // 假设我们想从页面左上角开始的某个区域提取文本
            RectangleF rect = new RectangleF(50, 50, 300, 100); 

            PdfTextExtractor extractor = new PdfTextExtractor(page);
            string regionText = extractor.ExtractText(rect);

            Console.WriteLine($"--- 从指定区域提取的文本 ---");
            Console.WriteLine($"区域 ({rect.X}, {rect.Y}, {rect.Width}, {rect.Height}) 的文本:");
            Console.WriteLine(regionText);

            doc.Close();
            Console.ReadKey();
        }
    }
}

这里的关键在于 RectangleF:

  • 它定义了要提取的页面区域。
  • extractor.ExtractText(rect) 只返回该矩形范围内的文本。

需要注意的是,区域提取高度依赖版式稳定性。如果同一类 PDF 的模板经常变化,坐标方案就要额外维护;如果版式固定,它反而是效率很高的字段抽取手段。

表格如何识别:从页面元素中还原结构化数据

相比纯文本提取,表格解析通常更难。原因在于 PDF 并没有真正意义上的“表格对象”,很多表格只是由文本、线条和矩形拼出来的视觉结构。程序要把这些页面元素重新理解成“行、列、单元格”,本身就是一次结构还原。

展示 PDF 表格识别后如何还原为行列结构的中文信息图
PDF 表格识别到结构化输出把 PDF 页面中的视觉表格,转换成可遍历的行列数据结构。

Spire.PDF 提供了专门的表格识别能力,适合把页面里的表格内容提取为可继续处理的数据结构。

识别页面中的表格并输出内容

下面的示例展示了如何识别 PDF 页面中的表格,并逐行逐列读取单元格文本:

using Spire.Pdf;
using Spire.Pdf.Tables;
using System;
using System.Text;

namespace PdfParserTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            string pdfFilePath = "Data/TableSample.pdf"; // 假设有一个包含表格的PDF文件
            PdfDocument doc = new PdfDocument();
            doc.LoadFromFile(pdfFilePath);

            // 获取第一页
            PdfPageBase page = doc.Pages[0];

            // 创建PdfTableExtractor实例
            PdfTableExtractor extractor = new PdfTableExtractor(page);

            // 识别并提取所有表格
            PdfTable[] tables = extractor.ExtractTable(true); // true表示尝试识别所有表格

            if (tables != null && tables.Length > 0)
            {
                Console.WriteLine($"在页面 {1} 中找到 {tables.Length} 个表格。");
                for (int i = 0; i < tables.Length; i++)
                {
                    PdfTable table = tables[i];
                    Console.WriteLine($"--- 表格 {i + 1} 内容 ---");

                    // 遍历表格的行和列
                    for (int r = 0; r < table.RowCount; r++)
                    {
                        StringBuilder rowData = new StringBuilder();
                        for (int c = 0; c < table.ColumnCount; c++)
                        {
                            // 获取单元格内容
                            string cellText = table.GetText(r, c);
                            rowData.Append($"{cellText,-20}"); // 格式化输出,确保对齐
                        }
                        Console.WriteLine(rowData.ToString());
                    }
                    Console.WriteLine();
                }
            }
            else
            {
                Console.WriteLine("未在页面中找到表格。");
            }

            doc.Close();
            Console.ReadKey();
        }
    }
}

这段代码里,和表格识别直接相关的核心点有三个:

  • PdfTableExtractor 用于页面表格识别。
  • ExtractTable(true) 会尝试提取当前页面中的所有表格。
  • PdfTable 提供 RowCount、ColumnCount 和 GetText(row, column),便于把结果继续转存到数据库、CSV 或业务对象中。

输出结果大致会是什么样

假设 TableSample.pdf 中包含如下简单表格:

Header 1Header 2Header 3
Data A1Data B1Data C1
Data A2Data B2Data C2

上述代码的控制台输出可能如下:

在页面 1 中找到 1 个表格。
--- 表格 1 内容 ---
Header 1            Header 2            Header 3            
Data A1             Data B1             Data C1             
Data A2             Data B2             Data C2             

如果你的需求是“把 PDF 里的表格导出来再继续算”,这一层结构化提取通常就是关键起点。

真正落地时要注意什么:性能、异常处理与资源释放

示例代码能跑通,只说明功能路径可行;要放进实际项目,还要考虑稳定性和处理成本。尤其是批量处理大文件、面对损坏 PDF 或格式不规范文档时,工程层面的细节会直接影响可用性。

展示 C# 批量解析 PDF 时的异常处理与资源释放要点信息图
PDF 解析落地时的稳定性检查把真实项目里最容易出问题的路径,整理成可执行的稳定性检查清单。

大文件处理时,先盯住性能和内存

  • 按需处理页面: 如果只需要某几页内容,就尽量避免无差别扫描整份文档。
  • 及时释放资源: 完成操作后调用 doc.Close(),或者根据项目风格改写为更清晰的资源管理方式,减少文件句柄和内存占用。
  • 批量任务可考虑并行: 当需要处理大量 PDF 文件时,多线程或并行任务能缩短总耗时,但前提是先评估机器资源与库本身的并发行为。

面对损坏、缺失或异常格式文件,错误处理不能省

真实环境里的 PDF 文件并不总是干净标准:可能路径错误、文件损坏、加密、结构异常,或者来源系统生成得并不规范。这类情况如果没有分层处理,批处理任务很容易中断。

using Spire.Pdf;
using System;
using System.IO;

namespace PdfParserTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            string pdfFilePath = "Data/CorruptedSample.pdf"; // 假设这是一个损坏或不存在的PDF
            PdfDocument doc = new PdfDocument();

            try
            {
                doc.LoadFromFile(pdfFilePath);
                Console.WriteLine($"PDF文档 '{pdfFilePath}' 已成功加载。总页数: {doc.Pages.Count}");
            }
            catch (FileNotFoundException)
            {
                Console.WriteLine($"错误: PDF文件 '{pdfFilePath}' 未找到。请检查文件路径。");
            }
            catch (PdfException ex)
            {
                Console.WriteLine($"PDF解析错误: {ex.Message}");
                // 记录更详细的错误信息到日志
                // Logger.LogError($"PDF解析失败: {ex.Message}", ex);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发生未知错误: {ex.Message}");
            }
            finally
            {
                doc.Close(); // 确保即使发生错误也尝试关闭文档
            }

            Console.ReadKey();
        }
    }
}

这段示例的意义不在于“多写几个 catch”,而在于把错误分层:

  • FileNotFoundException 用来处理路径或文件缺失问题。
  • PdfException 更适合承接 PDF 解析阶段的专属异常。
  • finally 中关闭文档,可以确保无论成功还是失败,都尽量把资源释放干净。

选择提取方案时,可以这样判断

  • 如果目标是检索全文、归档文本,优先用整页文本提取。
  • 如果文档模板固定,只关心少数字段,优先考虑坐标区域提取。
  • 如果核心数据以表格形式呈现,就直接走表格识别流程,并准备好对识别结果做二次清洗。

总结

对 C# 开发者来说,解析 PDF 的关键并不是自己处理复杂底层结构,而是借助成熟库,把“加载文档、提取文本、识别表格、处理异常”这些环节串成一条可复用的流程。以 Spire.PDF for .NET 为例,整页文本提取适合检索和归档,区域提取适合固定模板字段抽取,表格识别则适合把页面内容转回结构化数据。

如果你的项目已经遇到批量 PDF 处理需求,这套方法基本可以作为起步框架。下一步真正决定效果的,往往不是 API 会不会用,而是文档模板是否稳定、异常样本是否充分,以及你是否为性能和容错留出了工程空间。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。