PDFBoxで取得したテキストの空白置換

SlideShareがやっているように、PDFスライドから抽出したしたテキストから、改行やタブを半角スペース1つに置換を試みた際にはまったのでメモ。

取得したいのはSlideShareのページ下部に表示されているこの文字列。
f:id:n_agetsuma:20141025142450p:plain

1. PDFBoxでテキスト抽出

PDFTextStripper.getText(PDDocument doc) がIOExceptionを投げるため、IntStreamでループを回すとforEachブロック内での例外処理が強制される。シンプルにするためにfor文で書いてみる。

PDDocument doc = PDDocument.load("/Path/PDFSlide.pdf");
List<PDPage> pages = doc.getDocumentCatalog().getAllPages();
PDFTextStripper stripper = new PDFTextStripper();

for (int i = 1; i <= doc.getNumberOfPages(); i++) {         
    stripper.setStartPage(i);
    stripper.setEndPage(i);
    System.out.println(stripper.getText(doc));
}

この時点では以下のようにタブや空白、改行が混じったテキストが抽出される。

【JJUG	
  CCC	
  2014	
  Spring	
  H-­‐2】	
Javaトラブルに備えよう	
日本Javaユーザグループ 上妻 宜人 (あげつま のりと)	
はてなブログ : n-agetsuma.hatenablog.com	
2. 複数の空白やタブ・改行を半角スペースに置換

Javaで空白を置換する場合、replaceAllに¥sを設定する例が非常に多く紹介されているが、これではうまくいかないことがある。

// タブや改行を空白に置換し、2つ以上スペースが連続する場合は1つに変換
// ¥t タブ, ¥r 復帰, ¥n 改行, ¥r¥n 改行, ¥f 改ページ, 全角スペース
String text = stripper.getText(doc).trim()
                 .replaceAll("\\t|\\r|\\n|\\r\\n|\f| ", " ");
                 .replaceAll("\\s{2,}", " ");

JJUG と CCC の間の連続したスペースが消えない。

【JJUG  CCC  2014  Spring  H-­‐2】 Javaトラブルに備えよう...

調べてみると、JJUG{¥u0020}{¥u00a0}CCCとなっており、¥u0020はおなじみの半角スペースだが、¥u00a0(No-Break Space)が途中に挟まっている。

¥u00a0(No-Break Space)は、wikipediaに解説があり、HTMLでいう& nbspで、行末にスペースがある場合においてスペースと前の語の間で改行させないために使う文字。スライドの文字列には改行のコントロールが含まれているため、テキスト抽出すると普通のスペースではなく、nbspに変換される場合があるようだ。

他のスライドには同様な文字 ¥u202F (Narrow No-Break Space) も含まれていた。

対処

¥u00a0 (No-Break Space)¥u202F(Narrow No-Break Space) もスペースへの置換対象に加えてみた。

String text = stripper.getText(doc).trim()
                 .replaceAll("\\t|\\r|\\n|\\r\\n|\f| |\u00a0|\u202F", " ")
                 .replaceAll("\\s{2,}", " ");

1つの空白区切りで抽出できるようになった。

// before
【JJUG  CCC  2014  Spring  H-­‐2】 Javaトラブルに備えよう...
// after
【JJUG CCC 2014 Spring H-­‐2】 Javaトラブルに備えよう ...