如何让PDFBox提取PDF表格时保留空白字段?
问题:提取PDF表格时保留空白列数据的解决方案
需求
读取PDF文档中的表格数据,必须保留空白列的位置信息,确保能区分空白对应的是买入数量还是卖出数量等列。
问题
使用PDFBox提取表格文本时,空白列会被直接跳过,导致输出的字段顺序混乱,无法判断空白对应的具体列。比如原本买入数量为空、卖出数量有值的行,输出时会把卖出数量前移,和买入数量有值的行无法区分。
当前尝试
- 尝试设置
pdfStripper.setSortByPosition(true),无效 - 查找过相关问题案例,未找到有效解决方法
PDF中的表格结构
| 订单号 | 时间 | 买入数量 | 卖出数量 | 价格 | 已结算 |
|---|---|---|---|---|---|
| 1234567 | 09:23:45 | 250 | 354.4 | ||
| 1234589 | 13:38:10 | 300 | 400 | ||
| 1234677 | 14:28:15 | 100 | 980 | ||
| 1234722 | 15:45:50 | 265 | 770 |
当前输出(空白列丢失)
"1234567 09:23:45 250 354.4" "1234589 13:38:10 300 400" "1234677 14:28:15 100 980" "1234722 15:45:50 265 770"
期望输出(保留空白列位置)
"1234567 09:23:45 250 354.4 " "1234589 13:38:10 300 400 " "1234677 14:28:15 100 980 " "1234722 15:45:50 265 770 "
现有代码
String pdfText = ""; try { File file = new File("C:\\data.pdf"); PDDocument pdDocument = Loader.loadPDF(file); // Tried the below option as well, but does not help pdfStripper.setSortByPosition(true); pdfText = pdfStripper.getText(pdDocument); pdDocument.close(); } catch (IOException e) { e.printStackTrace(); } String[] arrayPDF = pdfText.split("\\r?\\n"); for (String data : arrayPDF) { System.out.println(data); }
解决方案建议
方法1:用PDFTextStripperByArea定位列边界
PDF表格的列有固定位置,先确定每一列的x坐标范围,逐行提取每个区域的文本,空白区域保留空字符串:
try (PDDocument doc = Loader.loadPDF(new File("C:\\data.pdf"))) { PDFTextStripperByArea stripper = new PDFTextStripperByArea(); // 示例坐标,需用PDFDebugger工具查看实际列位置调整 stripper.addRegion("orderId", new Rectangle2D.Double(10, 20, 80, 20)); stripper.addRegion("time", new Rectangle2D.Double(100, 20, 80, 20)); stripper.addRegion("buyQty", new Rectangle2D.Double(190, 20, 80, 20)); stripper.addRegion("sellQty", new Rectangle2D.Double(280, 20, 80, 20)); stripper.addRegion("price", new Rectangle2D.Double(370, 20, 80, 20)); stripper.addRegion("settled", new Rectangle2D.Double(460, 20, 80, 20)); PDPage page = doc.getPage(0); // 多页需循环处理 stripper.extractRegions(page); // 提取各区域文本,空区域返回空字符串 String orderId = stripper.getTextForRegion("orderId").trim(); String time = stripper.getTextForRegion("time").trim(); String buyQty = stripper.getTextForRegion("buyQty").trim(); String sellQty = stripper.getTextForRegion("sellQty").trim(); String price = stripper.getTextForRegion("price").trim(); String settled = stripper.getTextForRegion("settled").trim(); // 按固定格式拼接,保留空白位置 String line = String.format("%-8s %-8s %-8s %-8s %-8s %-8s", orderId, time, buyQty, sellQty, price, settled); System.out.println("\"" + line + "\""); } catch (IOException e) { e.printStackTrace(); }
方法2:使用专门的PDF表格提取库
若PDF结构复杂,推荐用专业表格提取工具,比如Tabula-java:
try { File pdfFile = new File("C:\\data.pdf"); PDDocument doc = Loader.loadPDF(pdfFile); PDPage page = doc.getPage(0); // Tabula表格提取逻辑 SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm(); List<Table> tables = sea.extract(page); for (Table table : tables) { List<List<Rectangle>> cells = table.getCells(); for (List<Rectangle> row : cells) { StringBuilder sb = new StringBuilder(); for (Rectangle cell : row) { String text = extractTextFromCell(page, cell).trim(); sb.append(String.format("%-8s", text)); } System.out.println("\"" + sb.toString() + "\""); } } doc.close(); } catch (IOException e) { e.printStackTrace(); } // 辅助方法:提取指定单元格文本 private static String extractTextFromCell(PDPage page, Rectangle cell) throws IOException { PDFTextStripperByArea stripper = new PDFTextStripperByArea(); stripper.addRegion("cell", new Rectangle2D.Double(cell.getX(), cell.getY(), cell.getWidth(), cell.getHeight())); stripper.extractRegions(page); return stripper.getTextForRegion("cell"); }
方法3:自定义文本位置映射
提取所有文本块的位置信息,按y坐标分组行、x坐标分组列,空白列填充空字符串:
try (PDDocument doc = Loader.loadPDF(new File("C:\\data.pdf"))) { PDFTextStripper stripper = new PDFTextStripper() { @Override protected void writeString(String text, List<TextPosition> textPositions) throws IOException { // 收集每个文本块的位置与内容 for (TextPosition tp : textPositions) { System.out.println("X: " + tp.getX() + ", Y: " + tp.getY() + ", Text: " + tp.getUnicode()); } } }; stripper.getText(doc); // 后续按y坐标分组行,x坐标匹配列,空白列填充空字符串 } catch (IOException e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者iCoder
相关产品推荐
相关产品推荐

