如何使用itextsharp从表结构PDF中读取数据?

2024-04-12

我在从 pdf 文件读取某些数据时遇到问题。
我的文件是结构化的,它包含表格和纯文本。标准解析器从同一行的不同列读取数据。例如:



Some Table Header  
Data Col1a     Data Col2a      Data Col3a
Data Col1b     Data Col2b      Data Col3b
               Data Col2c

  

用这个代码

        PdfReader reader = new PdfReader(pdfName);

        List<String> text = new List<String>();
        String page;
        List<String> pageStrings;
        string[] separators = { "\n", "\r\n" };

        for (int i = 1; i <= reader.NumberOfPages; i++)
        {
            page = PdfTextExtractor.GetTextFromPage(reader, i);
            pageStrings = new List<string>(page.Split(separators, StringSplitOptions.RemoveEmptyEntries));
            text.AddRange(pageStrings);

        }

        reader.Close();

        return text;

将被连接成字符串:



Some Table Header
Data Col1a Data Col2a Data Col3a  
Data Col1b Data Col2b Data Col3b  
Data Col2c  
  

我想要获得反映块中数据的串联字符串。我想为上面的示例获取这样的字符串:



Some Table Header
Data Col1a Data Col1b   
Data Col2a Data Col2b Data Col2c  
Data Col3a Data Col3b
  

有谁知道如何调整 itextsharp 以获得 pdf 解析器的这种行为? 也许有人有合适的代码示例?
示例 PDF 文件是here https://www.dropbox.com/s/jwsuu6mz9ez84ss/sampleFile.pdf?dl=0


OP 的示例文件包含多个部分,如下所示:

OP 在评论中提到:

另一种工具完全按照我想要的方式解析我的 PDF。 [...]

PS:这个工具是pdfbox

在此方法中使用PDFBox(v1.8.10,当前发布版本):

String extract(PDDocument document) throws IOException
{
    PDFTextStripper stripper = new PDFTextStripper();
    return stripper.getText(document);
}

返回上面显示的部分

Driver Book for 8/5/2015
Company IS MEDICAL; AND Date of Service IS BETWEEN 08/05/2015 AND 08/05/2015; AND Status IS Assigned; AND Vehicles IS  MEDICAL: 
CATY
 MEDICAL
Trip #: 314-A
Comments: ----LIVERY---
Destination:Pick-up:
Call Type: Livery
<Doctor Office>
REGO PARK,  (631) 
000-0000
(718) 896-5953
74- AVE 204E  HEIGHTS, NY 
11372 (718) 639-4154
11:00:00 PAT, MIKHAIL
Trip #: 314-B
Comments:  ----LIVERY---
Destination:Pick-up:
Call Type: Livery
74- AVE 204E  HEIGHTS, NY 
11372 (718) 639-4154
<Doctor Office>
63-6 REGO PARK, NY 
11374 (631) 000-0000
11:01:00 PAT, MIKHAIL

这并不是真正的整齐的按列提取,但某些信息块(如地址块)仍然保留在一起。

使用 iText(Sharp) 获得相同的输出实际上非常容易:只需显式使用SimpleTextExtractionStrategy而不是LocationTextExtractionStrategy这是默认使用的,即必须替换这一行

page = PdfTextExtractor.GetTextFromPage(reader, i);

by

page = PdfTextExtractor.GetTextFromPage(reader, i, new SimpleTextExtractionStrategy());

除了每个数据集有一个空格字符外(iText(Sharp) 提取Destination: Pick-up:代替Destination:Pick-up:)结果是相同的。


关于 PDFBox 提取文本的结论:

所以我认为 PDF 确实是表结构的。

实际上,这种提取顺序仅仅意味着在PDF页面内容流中绘制字符串段的操作按照这个顺序发生。由于根据 PDF 规范,这些操作的顺序是任意的,因此生成这些 PDF 的软件的任何更新都可能导致 PDFBox 无法使用这些文件PDFTextStripper 和 iTextSimpleTextExtractionStrategy仅仅提取出一堆难以理解的字符。


PS:如果设置了PDFBoxPDFTextStripper财产SortByPosition to true像这样

    PDFTextStripper stripper = new PDFTextStripper();
    stripper.setSortByPosition(true);
    return stripper.getText(document);

然后 PDFBox 像 iText(Sharp) 一样使用(默认)提取文本LocationTextExtractionStrategy does


OP 表示对内容流中固有的块结构感兴趣。最明显的结构(如通用 PDF 中的结构)是文本对象(其中可以绘制多个字符串)。

在手头的情况下SimpleTextExtractionStrategy用来。它可以轻松扩展,以在其输出中包含与文本对象的开头和结尾相对应的标记。在 Java 中,这可以通过使用匿名类来完成,如下所示:

return PdfTextExtractor.getTextFromPage(reader, pageNo, new SimpleTextExtractionStrategy()
{
    boolean empty = true;

    @Override
    public void beginTextBlock()
    {
        if (!empty)
            appendTextChunk("<BLOCK>");
        super.beginTextBlock();
    }

    @Override
    public void endTextBlock()
    {
        if (!empty)
            appendTextChunk("</BLOCK>\n");
        super.endTextBlock();
    }

    @Override
    public String getResultantText()
    {
        if (empty)
            return super.getResultantText();
        else
            return "<BLOCK>" + super.getResultantText();
    }

    @Override
    public void renderText(TextRenderInfo renderInfo)
    {
        empty = false;
        super.renderText(renderInfo);
    }
});

(文本提取.java https://github.com/mkl-public/testarea-itext5/blob/master/src/test/java/mkl/testarea/itext5/extract/TextExtraction.java method extractSimple)

(这个 Java 代码应该很容易翻译成 C#。empty布尔值可能看起来很有趣;不过,这是必要的,因为一旦将某些块附加到提取的内容,基类就假定要设置某些附加属性。)

使用这一扩展策略,我们可以得到上面所示的部分:

<BLOCK>Driver Book for 8/5/2015
Company IS MEDICAL; AND Date of Service IS BETWEEN 08/05/2015 AND 08/05/2015; AND Status IS Assigned; AND Vehicles IS  MEDICAL: 
CATY</BLOCK>
<BLOCK>
 MEDICAL</BLOCK>
<BLOCK>
Trip #: 314-A</BLOCK>
<BLOCK>
Comments: ----LIVERY---</BLOCK>
<BLOCK>
Destination: Pick-up:</BLOCK>
<BLOCK>
Call Type: Livery
<Doctor Office>
REGO PARK,  (631) 
000-0000
(718) 896-5953</BLOCK>
<BLOCK>
74- AVE 204E  HEIGHTS, NY 
11372 (718) 639-4154</BLOCK>
<BLOCK>
11:00:00</BLOCK>
<BLOCK> PAT, MIKHAIL</BLOCK>
<BLOCK>
Trip #: 314-B</BLOCK>
<BLOCK>
Comments:  ----LIVERY---</BLOCK>
<BLOCK>
Destination: Pick-up:</BLOCK>
<BLOCK>
Call Type: Livery
74- AVE 204E  HEIGHTS, NY 
11372 (718) 639-4154</BLOCK>
<BLOCK>
<Doctor Office>
63-6 REGO PARK, NY 
11374 (631) 000-0000</BLOCK>
<BLOCK>
11:01:00</BLOCK>
<BLOCK> PAT, MIKHAIL</BLOCK>

由于这将地址保留在同一块中,因此这可能在提取过程中有所帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用itextsharp从表结构PDF中读取数据? 的相关文章

随机推荐

  • 如何删除 git 中的本地存储库? [复制]

    这个问题在这里已经有答案了 我找不到命令 我尝试谷歌搜索 git 删除存储库 删除 git如果您只想删除与 git 相关的信息 分支 版本 请在存储库的根目录中删除该目录 如果你想删除所有内容 git data 代码等 只需删除整个目录即可
  • Gradle 包装器在 Windows 上的 Android 项目中获取错误的 Java 版本

    我在 Windows 上的 Android 项目中使用 gradle 当我执行时 gradlew version在我的项目路径中 错误显示为Could not determine java version from 12 我确实有已安装 J
  • 奇怪的错误,set::begin() 总是返回 const 迭代器

    为什么 set begin 总是返回一个 const 迭代器而不是标准迭代器 35 int test 36 std set
  • 在 MySQL 中将“描述”转换为“创建表”?

    我们可以从描述中获取 创建表 命令 describe 吗 我有一个表 其描述可以从 DESC TableName 获得 我想知道我是否可以了解该表是如何创建的 以便我可以使用相同的命令执行其他操作 我可以得到 sql dump 但我想知道是
  • F# 从 while 循环中中断

    有什么方法可以做到这一点C C 例如 C 风格 for int i 0 i lt 100 i if i 66 break 最简洁的答案是不 您通常会使用一些高阶函数来表达相同的功能 有许多函数可以让您执行此操作 对应于不同的模式 因此 如果
  • IP 地址的索引范围搜索算法

    给定一个包含 100 亿个以 CIDR 表示法表示的 IPv4 范围或两个 IP 之间的 ACL 列表 x x x x y x x x x y y y y 用于测试给定 IP 地址是否满足一个或多个 ACL 范围条件的有效搜索 索引算法是什
  • 在 MVP android 应用程序中演示者之间进行通信

    我正在使用 MVP 模式构建一个小型测试 Android 应用程序 我有两个片段片段 B 我用于滑动抽屉 和片段 A 主片段 两个片段都有自己的演示者 当我单击滑动绘制时 它应该发送消息或调用片段 A 中的方法来更新视图 我想问一下 两个片
  • 如何使用智能卡和 python 发出 TLS 请求?

    我尝试使用 python 库 请求 与受智能卡保护的网站进行通信 这意味着 SSL 中的强身份验证 您必须提供客户端证书 证书和私钥 由于我使用的是智能卡 因此我无法读取普通保护的私钥 只能读取模数 我可以使用 python 库 PyKCS
  • Firestore 分别按日期和时间查询

    如何按特定日期和时间范围查询我的文档 IE 我想按特定日期范围 01 01 2019 31 01 2019 查询文档 并且从这些日期仅查询上午 10 点到中午 12 点制作的文档 事情会是这样的 let ref db collection
  • Python将一维数组转换为二维数组[重复]

    这个问题在这里已经有答案了 我有一个清单 1 2 3 4 5 6 7 8 我想在 python 中将其转换为 1 2 3 4 5 6 7 8 有人可以帮我解决这个问题吗 接受输入 def chunks l n return l i i n
  • 声明 DNA 的新数据类型

    我从事生物学研究 特别是 DNA 并且经常存在来自基因组测序的数据大小的问题 对于那些没有生物学背景的人 我将快速概述 DNA 测序 DNA 由四个字母组成 A T G 和 C 它们的具体顺序决定了细胞中发生的情况 然而 DNA 测序技术的
  • GitHub 可以用于托管文件(mp3 和图像)吗?

    我正在寻找免费的文件托管服务 它可以让我获得静态链接到每个单独的文件easily 因此 文件 1 png 2 png 3 png 应分配给 URL www something com somepath 1 png http www some
  • Bourbon/Sass:#{$all-text-inputs} 带有悬停或焦点?

    根据波本文档 http thoughtbot com bourbon html5 input types 您可以使用 all text inputs 转动这个 all text inputs border 1px solid green 进
  • 是否可以选择以假的方式列出目标(可能带有描述)?

    在 Ruby RAKE 中 您可以通过以下方式记录您的任务 rakefile desc cleans temp task clean do p cleaning end desc compile the source task compil
  • 复制已过滤的数据子集:合并或事务复制?

    首先感谢您的阅读 我需要复制基于连接过滤器的数据子集 基于与其他表的联接的过滤器 Microsoft 使用联接过滤器 您可以将行过滤器从一个已发布的表扩展到另一个 这是设置 SQL Server 2012 事务复制订阅上的复制源 复制需要是
  • SSRS 2014 数据库设置 - 错误“使用其他版本的 SQL Server 作为报表数据源...”不受支持

    我正在 Windows Server 2012 R2 服务器上设置新的 SQL Server 2014 Enterprise Reporting Services 实例 在 Reporting Services 配置管理器中 当我选择要在其
  • 使更新进度面板位于中心

    我对更新进度面板有疑问 我想在屏幕中间安装更新进度面板 谁能建议我 这样做的想法是什么 您可以使用 css 来做到这一点
  • Environment.Exit 和 Main 中的简单返回 2 之间的区别

    从应用程序外部来看 两者之间有什么区别吗 Environment Exit 2 and static int Main return 2 最明显的区别是您可以从代码中的任何位置调用Environment Exit 除此之外 如果还有其他前台
  • 在 C# 中打印表单/用户控件

    我的计划 包含一个带有几个文本框和一个按钮的表单 默认打印机 设置为Adobe PDF在我的电脑上 My Goal 想要在用户单击 打印 按钮时截取表单 用户控件的屏幕截图 然后屏幕截图将以 pdf 格式保存在桌面上 我的问题 我的代码有以
  • 如何使用itextsharp从表结构PDF中读取数据?

    我在从 pdf 文件读取某些数据时遇到问题 我的文件是结构化的 它包含表格和纯文本 标准解析器从同一行的不同列读取数据 例如 Some Table Header Data Col1a Data Col2a Data Col3a Data C