如何使用 C# 阅读文档中的表格

This article was translated from English: Does it need improvement?
Translated
View the article in English

IronOCR 使 C# 开发人员能够使用先进的机器学习模型从 PDF 和图像中的表格中提取数据,使用 ReadDocumentAdvanced 方法既能处理带有基本单元格的简单表格,也能处理像发票这样带有合并单元格的复杂结构。

<! -- 待办事项:在此处添加图片 --> <! --介绍实现的示意图 --> <!--说明:说明代码概念的图表或截图 -->

使用普通 Tesseract 从表格中提取数据具有挑战性,因为文本通常位于单元格中,并且稀疏地分布在文档中。 不过,我们的库包括一个经过训练和微调的机器学习模型,可以准确地检测和提取表格数据。 无论是处理财务报告、库存清单还是发票数据,IronOCR 都能提供高效解析结构化数据的工具。

对于简单的表格,可依靠使用标准 OcrInput的直接表格检测。 对于更复杂的结构,我们独有的 ReadDocumentAdvanced 方法可提供强大的结果,有效地解析表格并提供数据。 这种先进的方法利用机器学习来理解表格布局、合并单元格和复杂的格式,而传统的 OCR 通常很难做到这一点。

快速入门:一次调用提取复杂表格单元格

在几分钟内即可开始运行--本示例展示了如何使用 ReadDocumentAdvanced 进行一次 IronOCR 调用,即可从复杂文档中获得详细的表格单元格数据。 它通过加载 PDF、应用高级表格检测并直接返回单元格信息列表来展示易用性。

Nuget Icon立即开始使用 NuGet 创建 PDF 文件:

  1. 使用 NuGet 包管理器安装 IronOCR

    PM > Install-Package IronOcr

  2. 复制并运行这段代码。

    var cells = new IronTesseract().ReadDocumentAdvanced(new OcrInput().LoadPdf("invoiceTable.pdf")).Tables.First().CellInfos;
  3. 部署到您的生产环境中进行测试

    立即开始在您的项目中使用 IronOCR,免费试用!
    arrow pointer

以下步骤指导您开始使用 IronOCR 读取表格:


如何从简单表格中提取数据?

ReadDataTables属性设置为 true 可启用使用 Tesseract 进行表检测。 这种方法适用于单元格边界清晰、无合并单元格的基本表格。 我创建了一个简单的表格 PDF 来测试这个功能,你可以从这里下载:' simple-table.pdf '。 可以使用此方法检测没有合并单元格的简单表格。 对于更复杂的表格,请参考下面介绍的方法。

标准表格检测方法对以下方面尤为有效:

  • 电子表格输出
  • 行/列结构一致的基本数据表
  • 包含表格数据的报告
  • 简单的库存清单

如果在一般情况下使用 PDF OCR 文本提取,此方法可与 IronOCR 更广泛的文档处理功能无缝集成。

:path=/static-assets/ocr/content-code-examples/how-to/read-table-in-document-with-tesseract.cs
using IronOcr;
using System;
using System.Data;

// Instantiate OCR engine
var ocr = new IronTesseract();

// Enable table detection
ocr.Configuration.ReadDataTables = true;

using var input = new OcrPdfInput("simple-table.pdf");
var result = ocr.Read(input);

// Retrieve the data
var table = result.Tables[0].DataTable;

// Print out the table data
foreach (DataRow row in table.Rows)
{
    foreach (var item in row.ItemArray)
    {
        Console.Write(item + "\t");
    }
    Console.WriteLine();
}
Imports Microsoft.VisualBasic
Imports IronOcr
Imports System
Imports System.Data

' Instantiate OCR engine
Private ocr = New IronTesseract()

' Enable table detection
ocr.Configuration.ReadDataTables = True

Dim input = New OcrPdfInput("simple-table.pdf")
Dim result = ocr.Read(input)

' Retrieve the data
Dim table = result.Tables(0).DataTable

' Print out the table data
For Each row As DataRow In table.Rows
	For Each item In row.ItemArray
		Console.Write(item & vbTab)
	Next item
	Console.WriteLine()
Next row
$vbLabelText   $csharpLabel

如何阅读复杂的发票表格?

<! -- 待办事项:在此处添加图片 --> <! --! 在 IronPDF 中显示读取发票示例结果的输出 --> -->。 <!--说明:显示代码执行输出或结果的截图 -->

在商业环境中,发票是比较常见的复杂表格之一。 发票是包含行和列数据的复杂表格,通常具有合并单元格、不同列宽和嵌套结构。 通过 IronOCR,我们利用 ReadDocumentAdvanced 方法对其进行了有效处理。 该过程包括扫描文档、识别表格结构和提取数据。 在本例中,我们使用"invoiceTable"文件来展示 IronOCR 如何检索发票中的所有信息。

ReadDocumentAdvanced 方法需要 IronOcr.Extensions.AdvancedScan 软件包与基本 IronOCR 软件包一起安装。 该扩展提供先进的机器学习功能,专门针对复杂的文档布局进行训练。

请注意 在 .NET Framework 上使用高级扫描功能需要项目在 x64 架构上运行。 进入项目配置,取消选中"首选 32 位"选项即可实现此目的。 请在以下故障排除指南中了解更多信息:"Advanced Scan on .NET Framework"。

:path=/static-assets/ocr/content-code-examples/how-to/read-table-in-document-with-ml.cs
using IronOcr;
using System.Linq;

// Instantiate OCR engine
var ocr = new IronTesseract();

using var input = new OcrInput();
input.LoadPdf("invoiceTable.pdf");

// Perform OCR
var result = ocr.ReadDocumentAdvanced(input);

var cellList = result.Tables.First().CellInfos;
Imports IronOcr
Imports System.Linq

' Instantiate OCR engine
Dim ocr = New IronTesseract()

Using input As New OcrInput()
    input.LoadPdf("invoiceTable.pdf")

    ' Perform OCR
    Dim result = ocr.ReadDocumentAdvanced(input)

    Dim cellList = result.Tables.First().CellInfos
End Using
$vbLabelText   $csharpLabel

该方法将文档中的文本数据分为两类:一类有边框,另一类没有边框。 对于带边框的内容,图书馆会根据表格的结构将其进一步细分为小节。 该方法擅长处理

  • 具有不同描述的发票细列项目
  • 多栏价格细目
  • 发货和账单地址块
  • 税金和总额计算部分
  • 页眉和页脚信息

结果如下所示。 由于此方法侧重于边框所包含的信息,任何跨越多行的合并单元格都将被视为单个单元格。

提取的数据是什么样的?

IronSoftware OCR 将装运发票中的表格数据提取为结构化分层格式

如何组织和处理提取的表格单元格?

在当前实现中,提取的单元格尚未正确组织。 但是,每个单元格都包含有价值的信息,例如 X 和 Y 坐标、尺寸等等。 利用这些数据,我们可以创建一个用于各种用途的辅助类。 单元信息包括

  • 精确的 X/Y 坐标定位
  • 宽度和高度尺寸
  • 文本内容
  • 信心评分
  • 单元格关系

通过这些详细信息,您可以以编程方式重建表格结构,并应用自定义逻辑进行数据提取。 您还可以使用这些坐标来定义特定区域,以便在后续操作中进行有针对性的 OCR 处理。

以下是一些基本辅助方法:

using System;
using System.Collections.Generic;
using System.Linq;

// A helper class to process table data by sorting cells based on coordinates
public static class TableProcessor
{
    // Method to organize cells by their coordinates (Y top to bottom, X left to right)
    public static List<CellInfo> OrganizeCellsByCoordinates(List<CellInfo> cells)
    {
        // Sort cells by Y (top to bottom), then by X (left to right)
        var sortedCells = cells
            .OrderBy(cell => cell.CellRect.Y)
            .ThenBy(cell => cell.CellRect.X)
            .ToList();

        return sortedCells;
    }

    // Example method demonstrating how to process multiple tables
    public static void ProcessTables(Tables tables)
    {
        foreach (var table in tables)
        {
            var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);

            Console.WriteLine("Organized Table Cells:");

            // Initialize previous Y coordinate
            int previousY = sortedCells.Any() ? sortedCells.First().CellRect.Y : 0;

            foreach (var cell in sortedCells)
            {
                // Print a new line if the Y-coordinate changes, indicating a new row
                if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
                {
                    Console.WriteLine();  // Start a new row
                    previousY = cell.CellRect.Y;
                }

                // Print the cell text followed by a tab
                Console.Write($"{cell.CellText}\t");
            }

            Console.WriteLine("\n--- End of Table ---");  // End of a table
        }
    }

    // Method to extract a specific row by the given index
    public static List<CellInfo> ExtractRowByIndex(TableInfo table, int rowIndex)
    {
        if (table == null || table.CellInfos == null || !table.CellInfos.Any())
        {
            throw new ArgumentException("Table is empty or invalid.");
        }

        var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);
        List<List<CellInfo>> rows = new List<List<CellInfo>>();

        // Group cells into rows based on Y coordinates
        int previousY = sortedCells.First().CellRect.Y;
        List<CellInfo> currentRow = new List<CellInfo>();

        foreach (var cell in sortedCells)
        {
            if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
            {
                // Store the completed row and start a new one
                rows.Add(new List<CellInfo>(currentRow));
                currentRow.Clear();

                previousY = cell.CellRect.Y;
            }

            currentRow.Add(cell);
        }

        // Add the last row if it wasn't added yet
        if (currentRow.Any())
        {
            rows.Add(currentRow);
        }

        // Retrieve the specified row
        if (rowIndex < 0 || rowIndex >= rows.Count)
        {
            throw new IndexOutOfRangeException($"Row index {rowIndex} is out of range.");
        }

        return rows[rowIndex];
    }
}
using System;
using System.Collections.Generic;
using System.Linq;

// A helper class to process table data by sorting cells based on coordinates
public static class TableProcessor
{
    // Method to organize cells by their coordinates (Y top to bottom, X left to right)
    public static List<CellInfo> OrganizeCellsByCoordinates(List<CellInfo> cells)
    {
        // Sort cells by Y (top to bottom), then by X (left to right)
        var sortedCells = cells
            .OrderBy(cell => cell.CellRect.Y)
            .ThenBy(cell => cell.CellRect.X)
            .ToList();

        return sortedCells;
    }

    // Example method demonstrating how to process multiple tables
    public static void ProcessTables(Tables tables)
    {
        foreach (var table in tables)
        {
            var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);

            Console.WriteLine("Organized Table Cells:");

            // Initialize previous Y coordinate
            int previousY = sortedCells.Any() ? sortedCells.First().CellRect.Y : 0;

            foreach (var cell in sortedCells)
            {
                // Print a new line if the Y-coordinate changes, indicating a new row
                if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
                {
                    Console.WriteLine();  // Start a new row
                    previousY = cell.CellRect.Y;
                }

                // Print the cell text followed by a tab
                Console.Write($"{cell.CellText}\t");
            }

            Console.WriteLine("\n--- End of Table ---");  // End of a table
        }
    }

    // Method to extract a specific row by the given index
    public static List<CellInfo> ExtractRowByIndex(TableInfo table, int rowIndex)
    {
        if (table == null || table.CellInfos == null || !table.CellInfos.Any())
        {
            throw new ArgumentException("Table is empty or invalid.");
        }

        var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);
        List<List<CellInfo>> rows = new List<List<CellInfo>>();

        // Group cells into rows based on Y coordinates
        int previousY = sortedCells.First().CellRect.Y;
        List<CellInfo> currentRow = new List<CellInfo>();

        foreach (var cell in sortedCells)
        {
            if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
            {
                // Store the completed row and start a new one
                rows.Add(new List<CellInfo>(currentRow));
                currentRow.Clear();

                previousY = cell.CellRect.Y;
            }

            currentRow.Add(cell);
        }

        // Add the last row if it wasn't added yet
        if (currentRow.Any())
        {
            rows.Add(currentRow);
        }

        // Retrieve the specified row
        if (rowIndex < 0 || rowIndex >= rows.Count)
        {
            throw new IndexOutOfRangeException($"Row index {rowIndex} is out of range.");
        }

        return rows[rowIndex];
    }
}
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Linq

' A helper class to process table data by sorting cells based on coordinates
Public Module TableProcessor
	' Method to organize cells by their coordinates (Y top to bottom, X left to right)
	Public Function OrganizeCellsByCoordinates(ByVal cells As List(Of CellInfo)) As List(Of CellInfo)
		' Sort cells by Y (top to bottom), then by X (left to right)
		Dim sortedCells = cells.OrderBy(Function(cell) cell.CellRect.Y).ThenBy(Function(cell) cell.CellRect.X).ToList()

		Return sortedCells
	End Function

	' Example method demonstrating how to process multiple tables
	Public Sub ProcessTables(ByVal tables As Tables)
		For Each table In tables
			Dim sortedCells = OrganizeCellsByCoordinates(table.CellInfos)

			Console.WriteLine("Organized Table Cells:")

			' Initialize previous Y coordinate
			Dim previousY As Integer = If(sortedCells.Any(), sortedCells.First().CellRect.Y, 0)

			For Each cell In sortedCells
				' Print a new line if the Y-coordinate changes, indicating a new row
				If Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8 Then
					Console.WriteLine() ' Start a new row
					previousY = cell.CellRect.Y
				End If

				' Print the cell text followed by a tab
				Console.Write($"{cell.CellText}" & vbTab)
			Next cell

			Console.WriteLine(vbLf & "--- End of Table ---") ' End of a table
		Next table
	End Sub

	' Method to extract a specific row by the given index
	Public Function ExtractRowByIndex(ByVal table As TableInfo, ByVal rowIndex As Integer) As List(Of CellInfo)
		If table Is Nothing OrElse table.CellInfos Is Nothing OrElse Not table.CellInfos.Any() Then
			Throw New ArgumentException("Table is empty or invalid.")
		End If

		Dim sortedCells = OrganizeCellsByCoordinates(table.CellInfos)
		Dim rows As New List(Of List(Of CellInfo))()

		' Group cells into rows based on Y coordinates
		Dim previousY As Integer = sortedCells.First().CellRect.Y
		Dim currentRow As New List(Of CellInfo)()

		For Each cell In sortedCells
			If Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8 Then
				' Store the completed row and start a new one
				rows.Add(New List(Of CellInfo)(currentRow))
				currentRow.Clear()

				previousY = cell.CellRect.Y
			End If

			currentRow.Add(cell)
		Next cell

		' Add the last row if it wasn't added yet
		If currentRow.Any() Then
			rows.Add(currentRow)
		End If

		' Retrieve the specified row
		If rowIndex < 0 OrElse rowIndex >= rows.Count Then
			Throw New IndexOutOfRangeException($"Row index {rowIndex} is out of range.")
		End If

		Return rows(rowIndex)
	End Function
End Module
$vbLabelText   $csharpLabel

表提取的最佳实践

在 IronOCR 中进行表格提取时,请考虑以下最佳实践:

1.文档质量:更高分辨率的文档能产生更好的效果。 对于扫描文件,请确保至少为 300 DPI。

2.预处理:对于质量较差或表格倾斜的文档,可考虑在处理前使用 IronOCR 的图像校正功能。

3.性能:对于包含多个表格的大型文档,请考虑使用多线程和异步支持来并行处理页面。

4.输出选项:提取表格数据后,您可以以各种格式导出结果。 了解有关数据输出选项以及如何从处理过的文档中创建可搜索 PDF 的更多信息。

5.流处理:对于网络应用程序或处理内存中文档的应用场景,请考虑使用 OCR for PDF streams 来避免文件系统操作。

摘要

IronOCR 通过基于 Tesseract 的标准检测和先进的机器学习方法提供强大的表格提取功能。 标准方法适用于简单的表格,而 ReadDocumentAdvanced 方法则适用于发票等复杂文档。 利用提供的辅助方法,您可以组织和处理提取的数据,以满足您的特定需求。

探索更多 IronOCR 功能,以增强您的文档处理工作流,并在您的 .NET 应用程序中充分发挥光学字符识别的潜力。

常见问题解答

如何用 C# 从 PDF 和图像中提取表格数据?

IronOCR 使 C# 开发人员能够使用先进的机器学习模型从 PDF 和图像中提取表格数据。对于简单的表格,可使用 OcrInput 类,并将 ReadDataTables 属性设置为 true。对于合并单元格的复杂表格,使用 ReadDocumentAdvanced 方法可获得更准确的结果。

简单表格提取和复杂表格提取有什么区别?

IronOCR 中的简单表格提取使用 Tesseract 的 ReadDataTables 属性,对于单元格边界清晰的基本表格效果很好。复杂的表格提取需要使用 ReadDocumentAdvanced 方法,该方法使用机器学习来处理合并单元格、发票和复杂格式。

如何从复杂的表格中快速提取数据?

在一次调用中使用 IronOCR 的 ReadDocumentAdvanced 方法:var cells = new IronTesseract().ReadDocumentAdvanced(new IronOcrInput().LoadPdf('invoiceTable.pdf')).Tables.First().CellInfos; 这将利用机器学习来理解表格布局和复杂格式。

哪些类型的文档最适合使用简单表格检测?

IronOCR 的简单表格检测方法在电子表格导出、具有一致行/列结构的基本数据表、包含表格数据的报告以及没有合并单元格的简单库存清单方面特别有效。

如何启用基本表格的表格检测?

要在 IronOCR 中启用基本表的表格检测功能,请将 ReadDataTables 属性设置为 true。这将使用 Tesseract 的表格检测功能,对于单元格边界清晰、无合并单元格的表格效果很好。

图书馆能否处理布局复杂的发票和财务报告?

是的,IronOCR 的 ReadDocumentAdvanced 方法专门用于处理发票和财务报告等复杂文档。它使用经过训练的机器学习模型来检测和提取具有合并单元格和复杂格式的表格中的数据。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

审核者
Jeff Fritz
Jeffrey T. Fritz
首席项目经理 - .NET 社区团队
Jeff 也是 .NET 和 Visual Studio 团队的首席项目经理。他是 .NET Conf 虚拟会议系列的执行制片人,并主持“Fritz and Friends”直播节目,每周两次与观众一起谈论技术并编写代码。Jeff 撰写研讨会、演示文稿并计划包括 Microsoft Build、Microsoft Ignite、.NET Conf 和 Microsoft MVP 峰会在内的最大型微软开发者活动的内容。
准备开始了吗?
Nuget 下载 5,384,824 | 版本: 2026.2 刚刚发布