我正在使用iTextSharp从pdfs中提取数据.我偶然发现了以下问题所描述的问题:
我创建了一个示例excel文件来说明.这是它的样子:
我将其转换为pdf,使用其中一个免费的在线转换器,生成一个类似的pdf(当我生成pdf时,我没有将样式应用于excel):
现在,使用iTextSharp
从pdf中提取数据,返回以下字符串作为提取的数据:
如您所见,包裹的单元格数据会生成新行,其中每个包裹的数据由一个空格分隔.
问题:现在,如何确定给定的包装数据所属的列?如果只iTextSharp
保留与列一样多的空格......
在我的示例中 - 如何识别111属于哪个列?
更新1:
只要字段有多个单词(即包含空格),就会出现类似的问题.例如,考虑上面示例的第1行:
说它看起来像
---A--- ---B--- ---C--- ---D--- aaaaaaa bb b cccc
iText将再次生成这个提取,如下所示:
aaaaaaa bb b cccc
这里的问题相同,必须确定每列的边界.
更新2: 我正在使用的真实pdf文件的示例: 这就是pdf数据的样子.
除了Chris的通用答案,iText(Sharp)内容解析的一些背景......
iText的(夏普)提供了一种用于内容提取框架在namespace iTextSharp.text.pdf.parser
/ package com.itextpdf.text.pdf.parser
.此franework读取页面内容,跟踪当前图形状态,并将有关内容的信息转发给IExtRenderListener
或IRenderListener
/ ExtRenderListener
或RenderListener
用户(即您)提供.尤其是它并不能解释为结构信息.
该渲染监听器可以是文本提取策略(ITextExtractionStrategy
/ TextExtractionStrategy
),即特殊的渲染监听器,其主要被设计为在没有格式化或布局信息的情况下提取纯文本流.对于这个特殊情况,iText(夏普)还提供了两个示例实现,即SimpleTextExtractionStrategy
和LocationTextExtractionStrategy
.
对于您的任务,您需要一个更复杂的渲染侦听器
使用坐标导出文本(Chris 在他的一个答案中提供了一个扩展LocationTextExtractionStrategy
,可以另外提供文本块的位置和边界框),允许您使用其他代码来分析表格结构; 要么
对表格数据本身进行分析.
我没有后一种变体的例子,因为一般地识别和解析表本身就是一个完整的项目.您可能想要了解Tabula项目的灵感; 这个项目非常擅长表格提取的任务.
PS:如果你更有家的感觉,试图提取其仍然试图反映原始布局内容的纯字符串表示结构化的内容,你可以尝试一些喜欢在提出这个答案的一个变种LocationTextExtractionStrategy
工作相似该pdftotext -layout
工具; 只显示要应用于的更改LocationTextExtractionStrategy
.
PPS:从非常具体的PDF表中提取数据可能要容易得多; 例如,看看这个答案,它表明在一些PDF分析之后,创建给定表的特定方式可能会产生一个简单的自定义渲染侦听器来提取表数据.这对于单个PDF来说是有意义的,其中表格跨越许多页面,就像答案的情况一样,或者如果您有许多由相同软件创建的PDF相同则可能有意义.
这就是我在您的问题评论中要求代表性样本文件的原因
关于你的意见
仍然使用上面的pdf示例,从头开始实现ITextExtractionStrategy和扩展LocationExtractionStrategy,我看到每个RenderText都在以下块中调用:Fi,el,d,A,Fi,el,d ...等等上.这可以改变吗?
您作为单独RenderText
呼叫获得的文本块不会被偶然或iText的随机决定分开.它们是页面内容中单独绘制的字符串!
在您的示例"Fi"中,"el","d"和"A"来自不同的RenderText
调用,因为内容流包含绘制第一个"Fi"的操作,然后是"el",然后是"d",然后是"一个".
起初这可能听起来很奇怪.对于这样拆掉了字的常见原因是PDF并没有使用从字体,字距信息; 因此,为了应用字距调整,PDF生成软件必须在字符之间插入微小的向前或向后跳跃,这些字符应该比不进行字距调整更远或更接近彼此.因此,单词通常在字距调整对之间被撕开.
所以这不能改变,你将得到那些碎片,文本提取策略的工作就是将它们组合在一起.
顺便说一句,有更糟糕的PDF,一些PDF生成器分别定位每个字形,最重要的是这些生成器主要构建GUI但可以作为一个功能自动导出GUI画布作为PDF.
我希望在进入"添加我自己的实现"的领域时,我可以控制如何确定什么是文本的"块".
你可以......嗯,你必须决定哪些传入的部分属于一起,哪些不属于.例如,具有相同y坐标的字形是否形成一条线?或者它们在不同的列中形成单独的行,这些行恰好位于彼此相邻的位置.
所以,是的,您决定将哪些字形解释为单个单词或单个表格单元格的内容,但您的输入包含实际PDF内容流中使用的字形组.
不仅如此,在界面的方法中我都没有"发现"它处理非文本数据/图像的方式/位置 - 所以我可以用间距问题进行调解(不调用RenderImage)
RenderImage
将要求嵌入式位图图像,JPEG等.如果您想了解矢量图形,您的策略还必须实现IExtRenderListener
哪些提供方法ModifyPath
,RenderPath
和ClipPath
.
这并不是真正的答案,但是我需要一个展示一些可以帮助您理解的东西。
从Excel,Word,PowerPoint,HTML或其他任何形式的第一个“转换”几乎总是一种破坏性的变化。该破坏性的部分是非常重要的,它是因为你是从具有(Excel)中的什么数据表示非常具体的知识,程序采集数据,而你把它变成绘图命令在一个很普通的通用格式(PDF),只有关心关于数据的外观,而不是数据本身。除非对数据进行“标记”(如今几乎从未如此),否则绘制命令将没有上下文。没有段落,没有句子,没有列,行,表等。从字面上看,只是在x,y
和在...画这个词a,b
。
其次,假设您的Excel文件具有以下数据,并且由于某种原因在制作PDF时最后一列比其他列窄:
Column A | Column B | Column C Data #1 Data #2 Data #3
您和我有上下文,因此我们知道第二和第四行实际上只是第一和第三行的延续。但是由于iText 在提取期间没有任何上下文,它不会这样想,它可以看到四行文本。实际上,由于它没有上下文,因此甚至看不到column,而只是行本身。
第三,尽管这是一件很小的事情,但您需要了解您不会在PDF中绘制空格。想象一下下面的三列表:
Column A | Column B | Column C Yes
如果您从PDF中提取数据,则会得到以下数据:
Column A | Column B | Column C Yes
在PDF内,“ Yes”一词将在x
您和我认为位于第三列下方的某个坐标处绘制,并且在其前面不会有很多空格。
正如我在开始时所说的,这并不是一个很好的答案,但希望它将为您解释您要解决的问题。如果您的PDF被标记,那么它将具有上下文,您可以在提取过程中使用该上下文。但是,上下文并不是通用的,因此通常不只是一个神奇的“插入上下文”复选框。Excel实际上确实有一个复选框(如果我没记错的话)可以在导出过程中制作带标签的PDF,它最终会使用类似于HTML的表标记来创建带标签的PDF。非常原始,但可以使用。但是,由您来解析此上下文。