在本書的前面所介紹的內容中,所處理的都是純文本文件。但是事實上,人們用于保存信息的文件并不是純文本格式。現在比較流行的文件存儲格式有Adobe公司的PDF和Microsoft的Word、Excel等。在處理這些文件的時候,不能簡單的從文件讀取字符,需要根據他們特殊的格式提取內容。本章就將對比較流行的PDF、Word和Excel格式的處理工具逐一進行介紹。
7.1 使用PDFBox處理PDF文檔
PDF全稱Portable Document Format,是Adobe公司開發的電子文件格式。這種文件格式與操作系統平臺無關,可以在Windows、Unix或Mac OS等操作系統上通用。
PDF文件格式將文字、字型、格式、顏色及獨立于設備和分辨率的圖形圖像等封裝在一個文件中。如果要抽取其中的文本信息,需要根據它的文件格式來進行解析。幸好目前已經有不少工具能幫助我們做這些事情。
7.1.1 PDFBox的下載
最常見的一種PDF文本抽取工具就是PDFBox了,訪問網址http://sourceforge.net/projects/pdfbox/,進入如圖7-1所示的下載界面。

圖7-1 PDFBox的下載頁面
讀者可以在該網頁下載其最新的版本。本書采用的是PDFBox-0.7.3版本。PDFBox是一個開源的Java PDF庫,這個庫允許你訪問PDF文件的各項信息。在接下來的例子中,將演示如何使用PDFBox提供的API,從一個PDF文件中提取出文本信息。
7.1.2 在Eclipse中配置
以下是在Eclipse中創建工程,并建立解析PDF文件的工具類的過程。
(1)在Eclipse的workspace中創建一個普通的Java工程:ch7。
(2)把下載的PDFBox-0.7.3.zip解壓,解壓后的目錄結構如圖7-2所示。

圖7-2 解壓后的PDFBox包
(3)進入external目錄下,可以看到,這里包括了PDFBox所有用到的外部包。復制下面的Jar包到工程ch7的lib目錄下(如還未建立lib目錄,則先創建一個)。
l bcmail-jdk14-132.jar
l bcprov-jdk14-132.jar
l checkstyle-all-4.2.jar
l FontBox-0.1.0-dev.jar
l lucene-core-2.0.0.jar
然后再從PDFBox的lib目錄下,復制PDFBox-0.7.3.jar到工程的lib目錄下。
(4)在工程上單擊右鍵,在彈出的快捷菜單中選擇“Build Path->Config Build Path->Add Jars”命令,把工程lib目錄下面的包都加入工程的Build Path。筆者機器上完整的工程目錄如圖7-3所示:

圖7-3 工程截圖
7.1.3 使用PDFBox解析PDF內容
在剛剛創建的Eclipse工程中,創建一個ch7.pdfbox包,并創建一個PdfboxTest類。該類包含一個getText方法,用于從一個PDF中獲取文本信息,其代碼如下。
public void geText(String file) throws Exception {
// 是否排序
boolean sort = false;
// pdf文件名
String pdfFile = file;
// 輸入文本文件名稱
String textFile = null;
// 編碼方式
String encoding = "UTF-8";
// 開始提取頁數
int startPage = 1;
// 結束提取頁數
int endPage = Integer.MAX_VALUE;
// 文件輸入流,生成文本文件
Writer output = null;
// 內存中存儲的PDF Document
PDDocument document = null;
try {
try {
// 首先當作一個URL來裝載文件,如果得到異常再從本地文件系統//去裝載文件
URL url = new URL(pdfFile);
document = PDDocument.load(url);
// 獲取PDF的文件名
String fileName = url.getFile();
// 以原來PDF的名稱來命名新產生的txt文件
if (fileName.length() > 4) {
File outputFile = new File(fileName.substring(0, fileName.length() - 4) + ".txt");
textFile = outputFile.getName();
}
} catch (MalformedURLException e) {
// 如果作為URL裝載得到異常則從文件系統裝載
document = PDDocument.load(pdfFile);
if (pdfFile.length() > 4) {
textFile = pdfFile.substring(0, pdfFile.length() - 4) + ".txt";
}
}
// 文件輸入流,寫入文件倒textFile
output = new OutputStreamWriter(new FileOutputStream(textFile), encoding);
// PDFTextStripper來提取文本
PDFTextStripper stripper = null;
stripper = new PDFTextStripper();
// 設置是否排序
stripper.setSortByPosition(sort);
// 設置起始頁
stripper.setStartPage(startPage);
// 設置結束頁
stripper.setEndPage(endPage);
// 調用PDFTextStripper的writeText提取并輸出文本
stripper.writeText(document, output);
} finally {
if (output != null) {
// 關閉輸出流
output.close();
}
if (document != null) {
// 關閉PDF Document
document.close();
}
}
}
在上面的代碼中,getText方法接收一個String類型的參數,指定要提取的PDF文件路徑。這個位置可以是一個URL或本地文件。然后函數調用PDFBox提供的PDFTextStripper類,設置提取過程中的一些屬性(如起始頁、是否排序等)。最后將文本提取并寫入文件。
7.1.4 運行效果
下面看一下這個函數的運行效果,在PdfboxTest加入一個main函數,其代碼如下。
public static void main(String[] args) {
PdfboxTest test = new PdfboxTest();
try {
// 取得C盤下的index.pdf的內容
test.geText("C:\\index.pdf");
} catch (Exception e) {
e.printStackTrace();
}
}
這里要處理一個index.pdf文件,該PDF文件的內容如圖7-4所示。

圖7-4 要解析的PDF文檔內容
通過PdfboxTest處理后的文本文件如圖7-5所示。

圖7-5 處理的結果
可以看到,PDF中的文本已經被提取出來,保存于文本文件中了。其中第4行的超鏈接部分“POI News WebBlog”,在文本文件中已經被替換成了普通的純文本。讀者可以根據PDFBox所提供的API文檔進一步查詢其他功能。
7.1.5 與Lucene的集成
PDFBox還提供和Lucene的集成,它提供了一套簡單的方法把PDF Documents加入到Lucene的索引中去,請看以下代碼:
Document lucenedocument = LucenePDFDocument.getDocument(…);
其中,LucenePDFDocument是PDFBox中提供的一個類,它的getDocument被重載為3個方法,分別接收一個File對象、InputStream對象或者URL對象作為參數,然后從該參數傳遞進來的PDF文件中,提取并生成Lucene的Document對象。
當通過PDFBox從一個PDF文檔中得到一個Lucene Document后,可以直接使用IndexWriter把它加到Lucene的index中。LucenePDFDocument自動從PDF文件中提取各種元數據Field,并把它們加入到Document中。它提取的信息如表7-1所示。
表7-1 PDFBox生成的Lucene Document格式
Lucene Field名稱
|
說明
|
path
|
文件系統路徑(如果文檔是從文件裝載)
|
url
|
URL地址(如果文檔是從網絡裝載)
|
contents
|
整個Document的內容,索引但不存儲
|
summary
|
Document的內容前500個字符
|
modified
|
最后修改時間
|
uid
|
Document的惟一ID
|
CreationDate
|
從PDF的meta-data獲取
|
Creator
|
從PDF的meta-data獲取
|
Keywords
|
從PDF的meta-data獲取
|
ModificationDate
|
從PDF的meta-data獲取
|
Producer
|
從PDF的meta-data獲取
|
Subject
|
從PDF的meta-data獲取
|
Trapped
|
從PDF的meta-data獲取
|
下面通過LucenePDFDocument,直接對PDF建立索引,在ch7.pdfbox包下面新建一個PdfLuceneTest類,該類的代碼如下。
public class PdfLuceneTest {
public static void main(String[] args) {
try {
// IndexWriter存放索引到d:\index下
IndexWriter writer = new IndexWriter("d:\\index",
new StandardAnalyzer(), true);
// LucenePDFDocument返回由PDF產生的Lucene Docuement
Document d = LucenePDFDocument
.getDocument(new File("C:\\index.pdf"));
// 寫入索引
writer.addDocument(d);
// 關閉索引文件流
writer.close();
// 讀取d:\index下的索引文件建立IndexSearcher
IndexSearcher searcher = new IndexSearcher("d:\\index");
// 對索引的contents Field進行查找關鍵詞poi
Term t = new Term("contents", "poi");
// 根據Term生成Query
Query q = new TermQuery(t);
// 搜索返回結果集
Hits hits = searcher.search(q);
// 打印結果集
for (int i = 0; i < hits.length(); i++) {
System.out.println(hits.doc(i));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
函數利用LucenePDFDocument的getDocument函數,從一個PDF文件直接返回一個Lucene的Document,其中包含有path、url、modified、contents、summary等Field,把它們直接寫入index,然后創建一個IndexSearcher,對contents字段經行檢索,查找關鍵詞“poi”(注意必須是小寫),程序的執行結果如圖7-6所示。

圖7-6 搜索代碼的運行結果