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

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

我们来谈谈如何读取文档中的表格。 使用纯 Tesseract 从表格中提取数据可能具有挑战性,因为文本通常位于单元格中,并且稀疏地分散在整个文档中。 但是,我们的库配备了一个机器学习模型,该模型经过训练和微调,可以准确地检测和提取表格数据。

对于简单的表格,您可以依赖直接的表格检测;而对于更复杂的结构,我们独有的ReadDocumentAdvanced方法可提供强大的结果,有效地解析表格并提供数据。

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

几分钟即可上手使用——本示例展示了如何使用 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 '。 可以使用此方法检测没有合并单元格的简单表格。 对于更复杂的表格,请参考下面描述的方法。

: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

阅读发票示例

在商业环境中,发票是比较常见的复杂表格之一。 发票本身就是复杂的表格,包含行和列数据。 使用 IronOCR,我们利用ReadDocumentAdvanced方法来完美地处理它们。 该过程包括扫描文档、识别表格结构和提取数据。 在这个例子中,我们将使用" invoiceTable.pdf "文件来展示IronOCR如何从发票中检索所有信息。

ReadDocumentAdvanced方法需要将IronOcr.Extensions.AdvancedScan软件包与基础 IronOCR 软件包一起安装。

[{i:( 在 .NET Framework 上使用高级扫描功能需要项目在 x64 架构上运行。 进入项目配置,取消选中"首选 32 位"选项即可实现此目的。 请参阅以下故障排除指南了解更多信息:". NET Framework 高级扫描"。 @@--bracket-close--@@@

: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;
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

该方法将文档中的文本数据分为两类:一类有边框,另一类没有边框。 对于带边框的内容,图书馆会根据表格的结构将其进一步细分为小节。 结果如下所示。 值得注意的是,由于此方法侧重于边界内的信息,因此任何跨越多行的合并单元格都将被视为单个单元格。

结果

在文档中读取表格

辅助类

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

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

常见问题解答

如何使用 C# 改进文档中的表格数据提取?

您可以通过使用 IronOCR 的机器学习模型来增强 C# 文档中的表格数据提取,该模型专门用于准确检测和提取复杂的表格数据。此方法比使用标准 OCR 工具(如 Tesseract)更为有效。

What is the purpose of the `ReadDocumentAdvanced` method in IronOCR?

The `ReadDocumentAdvanced` method in IronOCR is designed to handle complex tables by providing robust results through effective parsing and data extraction. It is particularly useful for dealing with tables with intricate structures.

要开始使用 IronOCR 提取表格,请下载 C# 库,准备文档,设置 ReadDataTables 属性启用表格检测,并针对复杂表格使用 ReadDocumentAdvanced 方法。

要开始使用 IronOCR 提取表格,请下载 C# 库,准备您的文档,通过设置 ReadDataTables 属性启用表格检测,并使用 ReadDocumentAdvanced 方法处理复杂表格。

IronOCR 中的高级表格提取需要适用于 Windows 的 `IronOcr.Extensions.AdvancedScan` 软件包,以有效管理复杂的表格结构。

如何使用 IronOCR 组织提取的表格数据?

IronOCR 提供辅助方法,通过坐标组织提取的表格数据,允许您处理多个表格并按索引提取特定行以便更好地管理数据。

使用 IronOCR 提取的表格单元格包含哪些元数据?

使用 IronOCR 提取的表格单元格包含元数据,如 X 和 Y 坐标、单元格尺寸以及每个单元格中的文本内容,支持详细的数据分析和组织。

使用 IronOCR 提取表格时如何确保 .NET Framework 兼容性?

要确保与 .NET Framework 的兼容性,请在使用 IronOCR 提取表格时确保您的项目在 x64 架构上运行,并在项目配置中取消选中“Prefer 32-bit”选项。

IronOCR 的文章讨论了如何使用 IronOCR 库读取文档中的表格,尤其是聚焦于复杂的表格结构。由于文本分散,使用标准 OCR 工具(如 Tesseract)从表格中提取数据可能困难。然而,IronOCR 引入了特别为准确表格数据提取微调的机器学习模型。该库提供了 `ReadDocumentAdvanced` 方法,专为处理复杂表格而设计,通过有效的解析和数据提取提供强大的结果。要开始使用 IronOCR,用户需要下载 C# 库,准备他们的文档,通过设置 `ReadDataTables` 属性启用表格检测,并针对复杂表格使用 `ReadDocumentAdvanced` 方法。该文章为简单和复杂表格提供了实例,特别指出复杂的表格提取需要适用于 Windows 的 `IronOcr.Extensions.AdvancedScan` 软件包。它还描述了通过坐标组织提取的单元格的方法和使用辅助类处理多个表格。提取的数据包括有价值的信息,如 X 和 Y 坐标,允许进一步处理和组织表格单元格。文章强调,尽管当前的实现不是完全有序,但每个单元格的元数据可以用来改进数据管理。

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,167,857 | Version: 2025.11 刚刚发布