77

表形式のデータを含む PDF ファイルを解析する必要があります。PDFBoxを使用してファイル テキストを抽出し、後で結果 (文字列) を解析しています。問題は、表形式のデータに対して期待どおりにテキスト抽出が機能しないことです。たとえば、次のようなテーブルを含むファイルがあります (7 つの列: 最初の 2 つの列には常にデータがあり、複雑さの列には 1 つのみ、データは 1 つのファイナンシングの列にしかありません):

+----------------------------------------------------------------+
| AIH | Value | Complexity                     | Financing       |
|     |       | Medium | High | Not applicable | MAC/Other | FAE |
+----------------------------------------------------------------+
| xyz | 12.43 | 12.34  |      |                | 12.34     |     |
+----------------------------------------------------------------+
| abc | 1.56  |        | 1.56 |                |           | 1.56|
+----------------------------------------------------------------+

次に、PDFBox を使用します。

PDDocument document = PDDocument.load(pathToFile);
PDFTextStripper s = new PDFTextStripper();
String content = s.getText(document);

これらの 2 行のデータは、次のように抽出されます。

xyz 12.43 12.4312.43
abc 1.56 1.561.56

最後の 2 つの数字の間に空白はありませんが、これは最大の問題ではありません。問題は、最後の 2 つの数字 (中、高、該当なし) の意味がわからないことです。MAC/その他、FAE? 数字とその列の間に関係はありません。

PDFBox ライブラリを使用する必要はないので、別のライブラリを使用するソリューションで問題ありません。私が望むのは、ファイルを解析し、解析された各数値が何を意味するかを知ることです。

4

19 に答える 19

20

使用可能な形式でデータを抽出するアルゴリズムを考案する必要があります。使用する PDF ライブラリに関係なく、これを行う必要があります。文字とグラフィックスは、一連のステートフルな描画操作によって描画されます。つまり、画面上のこの位置に移動し、文字 'c' のグリフを描画します。

メソッドを拡張org.apache.pdfbox.pdfviewer.PDFPageDrawerしてオーバーライドすることをお勧めしますstrokePath。そこから、水平線分と垂直線分の描画操作を傍受し、その情報を使用してテーブルの列と行の位置を決定できます。次に、テキスト領域を設定し、どの数字/文字/文字をどの領域に描画するかを決定するだけです。領域のレイアウトがわかっているので、抽出されたテキストがどの列に属しているかを知ることができます。

また、視覚的に分離されたテキスト間にスペースを入れない理由は、多くの場合、PDF でスペース文字が描画されないためです。代わりに、テキスト マトリックスが更新され、'move' の描画コマンドが発行されて、次の文字と最後の文字から「スペース幅」だけ離れて描画されます。

幸運を。

于 2010-08-12T21:47:34.290 に答える
16

PDFファイルからテーブルを抽出するために多くのツールを使用しましたが、うまくいきませんでした。

だから私は独自のアルゴリズム(その名前はtraprange)を実装して、pdfファイルの表形式データを解析しました。

次に、いくつかのサンプル PDF ファイルと結果を示します。

  1. 入力ファイル: sample-1.pdf、結果: sample-1.html
  2. 入力ファイル: sample-4.pdf、結果: sample-4.html

私のプロジェクト ページ ( traprange)にアクセスしてください。

于 2015-04-12T10:33:36.123 に答える
15

PDFBoxの領域ごとにテキストを抽出できます。Maven を使用している場合ExtractByArea.javaは、アーティファクトのサンプル ファイルを参照してください。pdfbox-examplesスニペットは次のようになります

   PDFTextStripperByArea stripper = new PDFTextStripperByArea();
   stripper.setSortByPosition( true );
   Rectangle rect = new Rectangle( 464, 59, 55, 5);
   stripper.addRegion( "class1", rect );
   stripper.extractRegions( page );
   String string = stripper.getTextForRegion( "class1" );

問題は、そもそも座標を取得することです。通常の を拡張し、各文字の座標をTextStripperオーバーライドprocessTextPosition(TextPosition text)して出力し、ドキュメント内のどこにあるかを把握することに成功しました。

しかし、少なくとも Mac を使用している場合は、もっと簡単な方法があります。プレビューで PDF を開き、⌘I を押してインスペクターを表示し、[トリミング] タブを選択して、単位が [ポイント] になっていることを確認し、[ツール] メニューから [四角形の選択] を選択して、関心のある領域を選択します。領域を選択すると、インスペクターに座標が表示されます。これを丸めてRectangleコンストラクター引数に入力できます。最初の方法を使用して、原点がどこにあるかを確認するだけです。

于 2013-06-30T22:02:48.600 に答える
11

私の答えには遅すぎるかもしれませんが、これはそれほど難しいことではないと思います。PDFTextStripper クラスを拡張して、writePage() および processTextPosition(...) メソッドをオーバーライドできます。あなたの場合、列ヘッダーは常に同じであると想定しています。つまり、各列見出しの x 座標がわかっているので、数値の x 座標を列見出しの x 座標と比較できます。それらが十分に近い場合(どれだけ近いかをテストする必要があります)、その数値はその列に属していると言えます。

別のアプローチは、各ページが書き込まれた後に「charactersByArticle」ベクターをインターセプトすることです。

@Override
public void writePage() throws IOException {
    super.writePage();
    final Vector<List<TextPosition>> pageText = getCharactersByArticle();
    //now you have all the characters on that page
    //to do what you want with them
}

列がわかれば、x 座標を比較して、すべての数値がどの列に属しているかを判断できます。

数字の間にスペースを入れない理由は、単語区切り文字列を設定する必要があるためです。

これがあなたや同様のことを試みている他の人にとって役立つことを願っています.

于 2012-09-22T17:15:19.980 に答える
4

pdftotextユーティリティ(sudo apt-get install poppler-utils)によって生成されたテキストファイルの解析でかなりの成功を収めました。

File convertPdf() throws Exception {
    File pdf = new File("mypdf.pdf");
    String outfile = "mytxt.txt";
    String proc = "/usr/bin/pdftotext";
    ProcessBuilder pb = new ProcessBuilder(proc,"-layout",pdf.getAbsolutePath(),outfile); 
    Process p = pb.start();

    p.waitFor();

    return new File(outfile);
}
于 2012-10-01T01:21:49.907 に答える
2

PDFからデータを抽出することは、問題を伴うことになります。ドキュメントはある種の自動プロセスで作成されていますか?その場合は、PDFを非圧縮のPostScriptに変換して(pdf2psを試してください)、PostScriptに悪用できる何らかの規則的なパターンが含まれているかどうかを確認することを検討してください。

于 2010-07-09T14:15:18.013 に答える
2

データが表形式のpdfファイルを読む際にも同じ問題がありました。PDFBox を使用して定期的に解析した後、各行はコンマを区切り文字として抽出されました...列の位置が失われました。これを解決するために、PDFTextStripperByArea を使用し、座標を使用して、各行の列ごとにデータを抽出しました。これは、固定形式の pdf がある場合に限ります。

        File file = new File("fileName.pdf");
        PDDocument document = PDDocument.load(file);
        PDFTextStripperByArea stripper = new PDFTextStripperByArea();
        stripper.setSortByPosition( true );
        Rectangle rect1 = new Rectangle( 50, 140, 60, 20 );
        Rectangle rect2 = new Rectangle( 110, 140, 20, 20 );
        stripper.addRegion( "row1column1", rect1 );
        stripper.addRegion( "row1column2", rect2 );
        List allPages = document.getDocumentCatalog().getAllPages();
        PDPage firstPage = (PDPage)allPages.get( 2 );
        stripper.extractRegions( firstPage );
        System.out.println(stripper.getTextForRegion( "row1column1" ));
        System.out.println(stripper.getTextForRegion( "row1column2" ));

それから行 2 など...

于 2014-11-14T16:18:50.313 に答える
0

画像に印刷してOCRを実行するのはどうですか?

ひどく効果がないように聞こえますが、実際には、テキストにアクセスできないようにすることがPDFの目的そのものであり、やらなければならないことをやらなければなりません。

于 2010-07-17T14:26:09.557 に答える
0

http://swftools.org/これらの人は pdf2swf コンポーネントを持っています。また、テーブルを表示することもできます。ソースも出しています。それで、あなたはおそらくそれをチェックすることができます.

于 2010-10-26T13:26:43.277 に答える
0

これは、pdfbox 2.0.6 を使用して PDF ファイルに「長方形のテーブルのみ」が含まれている場合に正常に機能します。長方形のテーブルのみの他のテーブルでは機能しません。

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.PDFTextStripperByArea;
public class PDFTableExtractor {
    public static void main(String[] args) throws IOException {
        ArrayList<String[]> objTableList = readParaFromPDF("C:\\sample1.pdf", 1,1,6);
        //Enter Filepath, startPage, EndPage, Number of columns in Rectangular table
    }
    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;
    }
}
于 2017-09-14T06:04:33.587 に答える
-1

PDFファイルからテーブルの内容を読み取るには、任意のAPIを使用してPDFファイルをテキストファイルに変換するだけで済みます(私はiTextのPdfTextExtracter.getTextFromPage()を使用しています)。その後、Javaプログラムでそのtxtファイルを読み取ります..それを読んだ後、主要なタスクは完了です..必要なデータをフィルタリングする必要があります。興味のあるレコードが見つかるまで、String クラスの split メソッドを継続的に使用することで、それを行うことができます.. これは、PDF ファイルでレコードの一部を抽出し、.CSV ファイルに書き込む私のコードです.. PDF の URLファイルは.. http://www.cea.nic.in/reports/monthly/generation_rep/actual/jan13/opm_02.pdf

コード:-

public static void genrateCsvMonth_Region(String pdfpath, String csvpath) {
        try {
            String line = null;
            // Appending Header in CSV file...
            BufferedWriter writer1 = new BufferedWriter(new FileWriter(csvpath,
                    true));
            writer1.close();
            // Checking whether file is empty or not..
            BufferedReader br = new BufferedReader(new FileReader(csvpath));

            if ((line = br.readLine()) == null) {
                BufferedWriter writer = new BufferedWriter(new FileWriter(
                        csvpath, true));
                writer.append("REGION,");
                writer.append("YEAR,");
                writer.append("MONTH,");
                writer.append("THERMAL,");
                writer.append("NUCLEAR,");
                writer.append("HYDRO,");
                writer.append("TOTAL\n");
                writer.close();
            }
            // Reading the pdf file..
            PdfReader reader = new PdfReader(pdfpath);
            BufferedWriter writer = new BufferedWriter(new FileWriter(csvpath,
                    true));

            // Extracting records from page into String..
            String page = PdfTextExtractor.getTextFromPage(reader, 1);
            // Extracting month and Year from String..
            String period1[] = page.split("PEROID");
            String period2[] = period1[0].split(":");
            String month[] = period2[1].split("-");
            String period3[] = month[1].split("ENERGY");
            String year[] = period3[0].split("VIS");

            // Extracting Northen region
            String northen[] = page.split("NORTHEN REGION");
            String nthermal1[] = northen[0].split("THERMAL");
            String nthermal2[] = nthermal1[1].split(" ");

            String nnuclear1[] = northen[0].split("NUCLEAR");
            String nnuclear2[] = nnuclear1[1].split(" ");

            String nhydro1[] = northen[0].split("HYDRO");
            String nhydro2[] = nhydro1[1].split(" ");

            String ntotal1[] = northen[0].split("TOTAL");
            String ntotal2[] = ntotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("NORTHEN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(nthermal2[4] + ",");
            writer.append(nnuclear2[4] + ",");
            writer.append(nhydro2[4] + ",");
            writer.append(ntotal2[4] + "\n");

            // Extracting Western region
            String western[] = page.split("WESTERN");

            String wthermal1[] = western[1].split("THERMAL");
            String wthermal2[] = wthermal1[1].split(" ");

            String wnuclear1[] = western[1].split("NUCLEAR");
            String wnuclear2[] = wnuclear1[1].split(" ");

            String whydro1[] = western[1].split("HYDRO");
            String whydro2[] = whydro1[1].split(" ");

            String wtotal1[] = western[1].split("TOTAL");
            String wtotal2[] = wtotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("WESTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(wthermal2[4] + ",");
            writer.append(wnuclear2[4] + ",");
            writer.append(whydro2[4] + ",");
            writer.append(wtotal2[4] + "\n");

            // Extracting Southern Region
            String southern[] = page.split("SOUTHERN");

            String sthermal1[] = southern[1].split("THERMAL");
            String sthermal2[] = sthermal1[1].split(" ");

            String snuclear1[] = southern[1].split("NUCLEAR");
            String snuclear2[] = snuclear1[1].split(" ");

            String shydro1[] = southern[1].split("HYDRO");
            String shydro2[] = shydro1[1].split(" ");

            String stotal1[] = southern[1].split("TOTAL");
            String stotal2[] = stotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("SOUTHERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(sthermal2[4] + ",");
            writer.append(snuclear2[4] + ",");
            writer.append(shydro2[4] + ",");
            writer.append(stotal2[4] + "\n");

            // Extracting eastern region
            String eastern[] = page.split("EASTERN");

            String ethermal1[] = eastern[1].split("THERMAL");
            String ethermal2[] = ethermal1[1].split(" ");

            String ehydro1[] = eastern[1].split("HYDRO");
            String ehydro2[] = ehydro1[1].split(" ");

            String etotal1[] = eastern[1].split("TOTAL");
            String etotal2[] = etotal1[1].split(" ");
            // Appending filtered data into CSV file..
            writer.append("EASTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(ethermal2[4] + ",");
            writer.append(" " + ",");
            writer.append(ehydro2[4] + ",");
            writer.append(etotal2[4] + "\n");

            // Extracting northernEastern region
            String neestern[] = page.split("NORTH");

            String nethermal1[] = neestern[2].split("THERMAL");
            String nethermal2[] = nethermal1[1].split(" ");

            String nehydro1[] = neestern[2].split("HYDRO");
            String nehydro2[] = nehydro1[1].split(" ");

            String netotal1[] = neestern[2].split("TOTAL");
            String netotal2[] = netotal1[1].split(" ");

            writer.append("NORTH EASTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(nethermal2[4] + ",");
            writer.append(" " + ",");
            writer.append(nehydro2[4] + ",");
            writer.append(netotal2[4] + "\n");
            writer.close();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }
于 2014-02-27T12:16:10.330 に答える
-1

私は PDFBox に慣れていませんが、 itext を見てみることができます。ホームページには PDF 生成と書かれていますが、PDF の操作と抽出も行うことができます。自分のユースケースに適合するかどうかを確認してください。

于 2010-07-08T13:53:26.693 に答える