You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现PDF表格转CSV:仅获取列名无数据的问题求助

问题:PDF表格转CSV仅获取到列名,无法读取数据内容

我需要将PDF表格文件转换为CSV文件,已编写如下Java代码,但运行后仅能获取到5个列名,无法获取对应的数据。代码如下:

public static ArrayList<String[]> readParaFromPDF(String pdfPath, int pageNoStart, int pageNoEnd, int noOfColumnsInTable) { 
    ArrayList<String[]> objArrayList = new ArrayList<>(); 
    try { 
        PDDocument document = PDDocument.load(new File(pdfPath)); 
        document.getClass(); 
        if (!document.isEncrypted()) { 
            PDFTextStripperByArea stripper = new PDFTextStripperByArea(); 
            stripper.setSortByPosition(true); 
            PDFTextStripper tStripper = new PDFTextStripper(); 
            tStripper.setStartPage(pageNoStart); 
            tStripper.setEndPage(pageNoEnd); 
            String pdfFileInText = tStripper.getText(document); 
            // split by whitespace 
            String Documentlines[] = pdfFileInText.split("\\r?\\n"); 
            for (String line : Documentlines) { 
                String lineArr[] = line.split("\\s+"); 
                if (lineArr.length == noOfColumnsInTable) { 
                    for (String linedata : lineArr) { 
                        System.out.print(linedata + " "); 
                    } 
                    System.out.println(""); 
                    objArrayList.add(lineArr); 
                } 
            } 
        } 
    } catch (Exception e) { 
        System.out.println("Exception " + e); 
    } 
    return objArrayList; 
}

解决方案

我看了你的代码,发现几个关键问题导致你只能拿到列名,读不到表格数据:

  1. 浪费了PDFTextStripperByArea的能力:你初始化了这个专门用来精准提取指定区域文本的工具(非常适合表格场景),但完全没用到它,反而用普通的PDFTextStripper把整个页面文本扒下来再按空格分割。这种方式在表格单元格包含空格时会直接拆碎内容,而且PDF里的文本换行、空格不一定和视觉上的表格行、单元格对齐,导致分割后的数组长度和列数不匹配,被你的if (lineArr.length == noOfColumnsInTable)过滤掉了。

  2. 简单分割逻辑太脆弱:PDF的文本提取结果和视觉排版不一定完全对应,尤其是复杂表格,靠换行和空格分割根本没法准确识别表格结构。

针对这些问题,给你两种可行的解决思路:

思路一:正确使用PDFTextStripperByArea提取表格

这个方法需要你先确定表格每一列在PDF页面上的坐标范围(可以用PDFBox自带的PDFDebugger工具查看坐标),然后给每个列定义专属区域,精准提取内容。

修改后的代码示例:

public static ArrayList<String[]> readParaFromPDF(String pdfPath, int pageNoStart, int pageNoEnd, int noOfColumnsInTable) {
    ArrayList<String[]> objArrayList = new ArrayList<>();
    // 用try-with-resources自动关闭文档,避免资源泄漏
    try (PDDocument document = PDDocument.load(new File(pdfPath))) { 
        if (!document.isEncrypted()) {
            PDFTextStripperByArea stripper = new PDFTextStripperByArea();
            stripper.setSortByPosition(true);

            // 这里需要根据你的PDF表格实际坐标调整!
            // 示例假设5列的x坐标范围分别是[0,100], [100,200], [200,300], [300,400], [400,500]
            // 用PDFDebugger打开PDF,就能看到表格列的具体坐标
            stripper.addRegion("col1", new Rectangle2D.Double(0, 0, 100, 792));
            stripper.addRegion("col2", new Rectangle2D.Double(100, 0, 100, 792));
            stripper.addRegion("col3", new Rectangle2D.Double(200, 0, 100, 792));
            stripper.addRegion("col4", new Rectangle2D.Double(300, 0, 100, 792));
            stripper.addRegion("col5", new Rectangle2D.Double(400, 0, 100, 792));

            // 遍历指定页码范围的页面
            for (int pageNum = pageNoStart; pageNum <= pageNoEnd; pageNum++) {
                PDPage page = document.getPage(pageNum - 1); // PDFBox页码从0开始
                stripper.extractRegions(page);

                // 提取每一列的文本并按行分割
                String[] col1Lines = stripper.getTextForRegion("col1").split("\\r?\\n");
                String[] col2Lines = stripper.getTextForRegion("col2").split("\\r?\\n");
                String[] col3Lines = stripper.getTextForRegion("col3").split("\\r?\\n");
                String[] col4Lines = stripper.getTextForRegion("col4").split("\\r?\\n");
                String[] col5Lines = stripper.getTextForRegion("col5").split("\\r?\\n");

                // 按行组合成表格数据
                int maxRows = Math.max(Math.max(col1Lines.length, col2Lines.length), 
                                      Math.max(col3Lines.length, Math.max(col4Lines.length, col5Lines.length)));
                for (int i = 0; i < maxRows; i++) {
                    String[] row = new String[noOfColumnsInTable];
                    row[0] = i < col1Lines.length ? col1Lines[i].trim() : "";
                    row[1] = i < col2Lines.length ? col2Lines[i].trim() : "";
                    row[2] = i < col3Lines.length ? col3Lines[i].trim() : "";
                    row[3] = i < col4Lines.length ? col4Lines[i].trim() : "";
                    row[4] = i < col5Lines.length ? col5Lines[i].trim() : "";
                    objArrayList.add(row);
                }
            }
        }
    } catch (Exception e) {
        System.err.println("处理PDF时出错: " + e.getMessage());
        e.printStackTrace();
    }
    return objArrayList;
}

思路二:使用专门的PDF表格提取库

如果你的表格结构复杂(比如有合并单元格、不规则列宽),手动定义坐标太麻烦,推荐用专门的表格提取库,比如Tabula-java或者PDFBox 2.0+自带的表格提取能力。

以Tabula-java为例,它能自动识别表格结构,代码更简洁:

// 需先在项目中引入Tabula-java的依赖(Maven/Gradle均可)
public static ArrayList<String[]> readTableFromPDF(String pdfPath, int pageNoStart, int pageNoEnd) {
    ArrayList<String[]> objArrayList = new ArrayList<>();
    try {
        TableExtractor extractor = new TableExtractor(pdfPath);
        extractor.setStartPage(pageNoStart);
        extractor.setEndPage(pageNoEnd);

        // 提取所有表格
        List<Table> tables = extractor.extractTables();
        for (Table table : tables) {
            // 遍历表格行,转换为数组
            for (List<Cell> rowCells : table.getRows()) {
                String[] row = new String[rowCells.size()];
                for (int i = 0; i < rowCells.size(); i++) {
                    row[i] = rowCells.get(i).getText().trim();
                }
                objArrayList.add(row);
            }
        }
    } catch (Exception e) {
        System.err.println("提取表格出错: " + e.getMessage());
        e.printStackTrace();
    }
    return objArrayList;
}

调试小技巧:

  • 用PDFTextStripperByArea时,先打印每个区域的提取结果,确认坐标是否正确。
  • 不管用哪种方法,先打印中间结果,看看文本提取是否符合预期,再调整逻辑。

内容的提问来源于stack exchange,提问作者user9282535

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:02:12