介绍 表数据结构是文档中最重要的数据结构之一,尤其是从企业系统导出数据时,数据通常采用表格式。
有几种数据文件格式,通常用于存储表格内容,例如CSV,文本和PDF。对于前两种格式,只需打开文件,循环浏览并使用适当的分隔符拆分单元格,就可以非常简单地进行操作。执行此操作的库很多。
对于PDF文件,故事完全不同,因为它没有针对表格内容的专用数据定义,如HTML中的表格,tr,td标签。PDF是一种复杂的格式,具有文本数据,字体,样式以及图像,音频和视频,可以将它们混合在一起。以下是我针对高密度表格内容中的数据提出的解决方案。
如何检测表格 经过一番调查,我意识到:
Column
Row
1. {"Ledger_ID", "|", "Sales Ledger Account", "FK to this customer's record to"} 2. {NULL, NULL, NULL, "Ledgers table"}
PDFBox API 我在traprange后面的库是PDFBox到目前为止我所知道的最好的PDF库。要从PDF文件提取文本,PDFBoxAPI提供了4个类:
PDDocument
PDPage
document.getDocumentCatalog().getAllPages().get(pageIdx: int)
TextPosition:表示文档中的单个单词或字符。我们可以通过class中的重写方法来获取a的所有TextPosition对象。一个对象有方法,,,返回其在网页和方法结合来获得它的内容。PDPageprocessTextPosition(text: TextPosition)PDTextStripperTextPositiongetX()getY()getWidth()getHeight()getCharacter() 在我的工作中,我直接通过使用TextPosition对象来处理文本块。对于PDF文件中的每个文本块,它将返回具有以下属性的文本元素:
TextPosition
PDPageprocessTextPosition(text: TextPosition)PDTextStripperTextPositiongetX()getY()getWidth()getHeight()getCharacter()
x:距页面左侧的水平距离
Trap Ranges 最重要的是确定每个行和列的边界,因为如果我们知道行/列的边界,我们可以从中检索该行/列中的所有文本,从而可以轻松提取表中的所有内容并将其放入结构化模型。我们将这些边界命名为trap-ranges。TrapRange有两个属性:
lowerBound:包含此范围的下限端点 upperBound:包含此范围的上端点要计算的值trap-ranges,我们循环浏览页面的所有文本,并将每个文本的范围投影到水平和垂直轴上,获取结果并将它们连接在一起。遍历页面的所有文本后,我们将计算陷阱范围,并使用它们来识别表格的单元格数据。 加入样品
lowerBound
upperBound
trap-ranges
Algorithm 1:计算每个PDF页面的陷印范围:
Algorithm 1
columnTrapRanges <-- [] rowTrapRanges <-- [] for each text in page begin columnTrapRanges <-- join(columnTrapRanges, {text.x, text.x + text.width} ) rowTrapRanges <-- join(rowTrapRanges, {text.y, text.y + text.height} ) end
在trap-ranges为表格计算之后,我们再次遍历所有文本并将它们分类为表格的正确单元格。
Algorithm 2:将文本块分类为正确的单元格:
Algorithm 2
table <-- new Table() for each text in page begin rowIdx <-- in rowTrapRanges, get index of the range that containts this text columnIdx <-- in columnTrapRanges, get index of the range that contains this text table.addText(text, rowIdx, columnIdx) end
TrapRangeBuilder:build()
Table,TableRow和TableCell
PDFTableExtractor
setSource
setSource(InputStream)
setSource(File)
setSource(String)
addPage
exceptPage
exceptLine
extract
例子
PDFTableExtractor extractor = new PDFTableExtractor(); List<Table> tables = extractor.setSource(“table.PDF”) .addPage(0) .addPage(1) .exceptLine(0) //the first line in each page .exceptLine(1) //the second line in each page .exceptLine(-1)//the last line in each page .extract(); String html = tables.get(0).toHtml();//table in html format String csv = tables.get(0).toString();//table in csv format using semicolon as a delimiter
评估 在实验中,我使用了具有高表内容密度的PDF文件。结果表明,我的实现检测表格的含量比其他开放式源更好:pdftotext,pdftohtml,pdf2table。对于具有多个表或太多嘈杂数据的文档,我的方法效果不佳。如果行的单元格重叠,则这些单元格的列将合并。
结论 TrapRange该方法对表数据密度高的PDF文件效果最佳。带有文档的多表或嘈杂的数据太多,TrapRange不是一个很好的选择。通过替换PDFBox为相应的PDF库或使用命令行工具pdftohtml提取文本块并将这些数据用作的输入数据,我的方法也可以用其他编程语言实现algorithm 1, 2。
原文链接:http://codingdict.com