1. 程式人生 > >使用POI來處理Excel和Word檔案格式

使用POI來處理Excel和Word檔案格式

Microsoft的Office系列產品擁有大量的使用者,Word、Excel也成為辦公檔案的首選。在Java中,已經有很多對於Word、Excel的開源的解決方案,其中比較出色的是Apache的Jakata專案的POI子專案。該專案的官方網站是http://jakarta.apache.org/poi/。

POI包括一系列的API,它們可以操作基於MicroSoft OLE 2 Compound Document Format的各種格式檔案,可以通過這些API在Java中讀寫Excel、Word等檔案。POI是完全的Java Excel和Java Word解決方案。POI子專案包括:POIFS、HSSF、HDF、HPSF。表7-2對它們進行了簡要介紹。

表7-2 POI子專案介紹

子專案名
說明

POIFS(POI File System)
POIFS是POI專案中最早的最基礎的一個模組,是Java到OLE 2 Compound Document Format的介面,支援讀寫功能,所有的其他專案都依賴與該專案。

HSSF(Horrible Spreadsheet Format)
HSSF是Java到Microsoft Excel 97(-2002)檔案的介面,支援讀寫功能

HWPF(Horrible Word Processing Format)
HWPF是Java到Microsoft Word 97檔案的介面,支援讀寫功能,但目前該模組還處於剛開始開發階段,只能實現一些簡單檔案的操作,在後續版本中,會提供更強大的支援

HPSF(Horrible Property Set Format)
HPSF 是Java到OLE 2 Compound Document Format檔案的屬性設定的介面,屬性設定通常用來設定文件的屬性(標題,作者,最後修改日期等),還可以設定使用者定義的屬性。HPSF支援讀寫功能,當前釋出版本中直支援讀功能。



7.3.1 對Excel的處理類
下面通過HSSF提供的介面對Excel檔案經行處理。首先需要下載POI的包,可以到apache的官方網站下載,地址為:http://apache.justdn.org/jakarta/poi/,本書採用的是poi-2.5.1-final-20040804.jar,讀者可以下載當前的穩定版本。把下載的包按照前面介紹的方式加入Build Path,然後新建一個ch7.poi包,並建立一個ExcelReader類。

ExcelReader類可以讀取一個XLS檔案,然後將其內容逐行提取出來,寫入文字檔案。其程式碼如下。

程式碼7.6

public class ExcelReader {

// 建立檔案輸入流

private BufferedReader reader = null;

// 檔案型別

private String filetype;

// 檔案二進位制輸入流

private InputStream is = null;

// 當前的Sheet

private int currSheet;

// 當前位置

private int currPosition;

// Sheet數量

private int numOfSheets;

// HSSFWorkbook

HSSFWorkbook workbook = null;

// 設定Cell之間以空格分割

private static String EXCEL_LINE_DELIMITER = " ";

// 設定最大列數

private static int MAX_EXCEL_COLUMNS = 64;

// 建構函式建立一個ExcelReader

public ExcelReader(String inputfile) throws IOException, Exception {

// 判斷引數是否為空或沒有意義

if (inputfile == null || inputfile.trim().equals("")) {

throw new IOException("no input file specified");

}

// 取得檔名的字尾名賦值給filetype

this.filetype = inputfile.substring(inputfile.lastIndexOf(".") + 1);

// 設定開始行為0

currPosition = 0;

// 設定當前位置為0

currSheet = 0;

// 建立檔案輸入流

is = new FileInputStream(inputfile);

// 判斷檔案格式

if (filetype.equalsIgnoreCase("txt")) {

// 如果是txt則直接建立BufferedReader讀取

reader = new BufferedReader(new InputStreamReader(is));

}

else if (filetype.equalsIgnoreCase("xls")) {

// 如果是Excel檔案則建立HSSFWorkbook讀取

workbook = new HSSFWorkbook(is);

// 設定Sheet數

numOfSheets = workbook.getNumberOfSheets();

}

else {

throw new Exception("File Type Not Supported");

}

}

// 函式readLine讀取檔案的一行

public String readLine() throws IOException {

// 如果是txt檔案則通過reader讀取

if (filetype.equalsIgnoreCase("txt")) {

String str = reader.readLine();

// 空行則略去,直接讀取下一行

while (str.trim().equals("")) {

str = reader.readLine();

}

return str;

}

// 如果是XLS檔案則通過POI提供的API讀取檔案

else if (filetype.equalsIgnoreCase("xls")) {

// 根據currSheet值獲得當前的sheet

HSSFSheet sheet = workbook.getSheetAt(currSheet);

// 判斷當前行是否到但前Sheet的結尾

if (currPosition > sheet.getLastRowNum()) {

// 當前行位置清零

currPosition = 0;

// 判斷是否還有Sheet

while (currSheet != numOfSheets - 1) {

// 得到下一張Sheet

sheet = workbook.getSheetAt(currSheet + 1);

// 當前行數是否已經到達檔案末尾

if (currPosition == sheet.getLastRowNum()) {

// 當前Sheet指向下一張Sheet

currSheet++;

continue;

} else {

// 獲取當前行數

int row = currPosition;

currPosition++;

// 讀取當前行資料

return getLine(sheet, row);

}

}

return null;

}

// 獲取當前行數

int row = currPosition;

currPosition++;

// 讀取當前行資料

return getLine(sheet, row);

}

return null;

}

// 函式getLine返回Sheet的一行資料

private String getLine(HSSFSheet sheet, int row) {

// 根據行數取得Sheet的一行

HSSFRow rowline = sheet.getRow(row);

// 建立字元創緩衝區

StringBuffer buffer = new StringBuffer();

// 獲取當前行的列數

int filledColumns = rowline.getLastCellNum();

HSSFCell cell = null;

// 迴圈遍歷所有列

for (int i = 0; i < filledColumns; i++) {

// 取得當前Cell

cell = rowline.getCell((short) i);

String cellvalue = null;

if (cell != null) {

// 判斷當前Cell的Type

switch (cell.getCellType()) {

// 如果當前Cell的Type為NUMERIC

case HSSFCell.CELL_TYPE_NUMERIC: {

// 判斷當前的cell是否為Date

if (HSSFDateUtil.isCellDateFormatted(cell)) {

// 如果是Date型別則,取得該Cell的Date值

Date date = cell.getDateCellValue();

// 把Date轉換成本地格式的字串

cellvalue = cell.getDateCellValue().toLocaleString();

}

// 如果是純數字

else {

// 取得當前Cell的數值

Integer num = new Integer((int) cell

.getNumericCellValue());

cellvalue = String.valueOf(num);

}

break;

}

// 如果當前Cell的Type為STRIN

case HSSFCell.CELL_TYPE_STRING:

// 取得當前的Cell字串

cellvalue = cell.getStringCellValue().replaceAll("'", "''");

break;

// 預設的Cell值

default:

cellvalue = " ";

}

} else {

cellvalue = "";

}

// 在每個欄位之間插入分割符

buffer.append(cellvalue).append(EXCEL_LINE_DELIMITER);

}

// 以字串返回該行的資料

return buffer.toString();

}

// close函式執行流的關閉操作

public void close() {

// 如果is不為空,則關閉InputSteam檔案輸入流

if (is != null) {

try {

is.close();

} catch (IOException e) {

is = null;

}

}

// 如果reader不為空則關閉BufferedReader檔案輸入流

if (reader != null) {

try {

reader.close();

} catch (IOException e) {

reader = null;

}

}

}

}

7.3.2 ExcelReader的執行效果
下面建立一個main函式,用來測試上面的ExcelReader類,程式碼如下。

程式碼7.7

public static void main(String[] args) {

try{

ExcelReader er=new ExcelReader("c:\\xp.xls");

String line=er.readLine();

while(line != null){

System.out.println(line);

line=er.readLine();

}

er.close();

}catch(Exception e){

e.printStackTrace();

}

}

main函式先建立一個ExcelReader類,然後呼叫它提供的介面readLine,對XLS檔案進行讀取,列印到控制檯,處理前的XLS檔案如圖7-12所示。


圖7-12 處理前的XLS檔案內容

執行main函式進行內容提取後,Eclipse的控制檯輸出如圖7-13所示。


圖7-13 輸出結果

可以看到,Excel檔案中的內容已經被成功的輸出了出來。

7.3.3 POI中Excel檔案Cell的型別
在讀取每一個Cell的值的時候,通過getCellType方法獲得當前Cell的型別,在Excel中Cell有6種類型,如表7-3所示。

表7-3 Cell的型別

CellType
說明

CELL_TYPE_BLANK
空值

CELL_TYPE_BOOLEAN
布林型

CELL_TYPE_ERROR
錯誤

CELL_TYPE_FORMULA
公式型

CELL_TYPE_STRING
字串型

CELL_TYPE_NUMERIC
數值型



本例採用了CELL_TYPE_STRING和CELL_TYPE_NUMERIC型別,因為在Excel檔案中只有字串和數字。如果Cell的Type為CELL_TYPE_NUMERIC時,還需要進一步判斷該Cell的資料格式,因為它有可能是Date型別,在Excel中的Date型別也是以Double型別的數字儲存的。Excel中的Date表示當前時間與1900年1月1日相隔的天數,所以需要呼叫HSSFDateUtil的isCellDateFormatted方法,判斷該Cell的資料格式是否是Excel Date型別。如果是,則呼叫getDateCellValue方法,返回一個Java型別的Date。

實際上Excel的資料格式有很多,還支援使用者自定義的型別,在Excel中,選擇一個單元格然後右鍵選擇“設定單元格格式”,在彈出的單元格格式中選中“數字”,如圖7-14所示。


圖7-14 Excel的單元格格式

圖中的資料有數值、貨幣、時間、日期、文字等格式。這些資料格式在POI中的HSSFDataFormat類裡都有相應的定義。

HSSFDataFormat是HSSF子專案裡面定義的一個類。類HSSFDataFormat允許使用者新建資料格式型別。HSSFDataFormat類包含靜態方法static java.lang.String getBuiltinFormat(short index),它可以根據編號返回內建資料型別。另外static short getBuiltinFormat(java.lang.String format)方法則可以根據資料型別返回其編號,static java.util.List getBuiltinFormats()可以返回整個內建的資料格式列表。

在HSSFDataFormat裡一共定義了49種內建的資料格式,如表7-4所示。

表7-4 HSSFDataFormat的資料格式

內建資料型別
編號

"General"
0

"0"
1

"0.00"
2

"#,##0"
3

"#,##0.00"
4

"($#,##0_);($#,##0)"
5

"($#,##0_);[Red]($#,##0)"
6

"($#,##0.00);($#,##0.00)"
7

"($#,##0.00_);[Red]($#,##0.00)"
8

"0%"
9

"0.00%"
0xa

"0.00E+00"
0xb

"# ?/?"
0xc

"# ??/??"
0xd

"m/d/yy"
0xe

"d-mmm-yy"
0xf

"d-mmm"
0x10

"mmm-yy"
0x11

"h:mm AM/PM"
0x12

"h:mm:ss AM/PM"
0x13

"h:mm"
0x14

"h:mm:ss"
0x15

"m/d/yy h:mm"
0x16

保留為過國際化用
0x17 - 0x24

"(#,##0_);(#,##0)"
0x25

"(#,##0_);[Red](#,##0)"
0x26

"(#,##0.00_);(#,##0.00)"
0x27

"(#,##0.00_);[Red](#,##0.00)"
0x28

"_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)"
0x29

"_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)"
0x2a

"_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)"
0x2b

"_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)"
0x2c

"mm:ss"
0x2d

"[h]:mm:ss"
0x2e

"mm:ss.0"
0x2f

"##0.0E+0"
0x30

"@" - This is text format
0x31


在上面表中,字串型別所對應的是資料格式為"@"(最後一行),也就是HSSFDataFormat中定義的值為0x31(49)的那行。Date型別的值的範圍是0xe-0x11,本例子中的Date格式為""m/d/yy"",在HSSFDataFormat定義的值為0xe(14)。

需要注意的一點是,所建立的Excel必須是在Microsoft Excel 97到Excel XP的版本上的,如果在Excel 2003中建立檔案後,在使用POI進行解析時,可能會出現問題。它會把Date型別當作自定義型別。POI目前只提供對Microsoft Excel XP以下的版本的支援,在以後的版本中,希望會提供對Microsoft Excel 2003更好的支援。

7.3.4 對Word的處理類
除了支援對Excel檔案的讀取外,POI還提供對Word的DOC格式檔案的讀取。但在它的發行版本中沒有釋出對Word支援的模組,需要另外下載一個POI的擴充套件的Jar包。使用者可以到http://www.ibiblio.org/maven2/org/textmining/tm-extractors/0.4/下載,本書採用的是tm-extractors-0.4_zip。

下載後,把該包加入工程的Build Path中,然後在ch7.poi包下新建一個類WordReader,該類提供一個靜態方法readDoc,讀取一個DOC檔案並返回文字。函式內容很簡單,就是呼叫WordExtractor的API來提取DOC的內容到字串,該函式的程式碼如下。

程式碼7.8

public static String readDoc(String doc) throws Exception {

// 建立輸入流讀取DOC檔案

FileInputStream in = new FileInputStream(new File(doc));

WordExtractor extractor = null;

String text = null;

// 建立WordExtractor

extractor = new WordExtractor();

// 對DOC檔案進行提取

text = extractor.extractText(in);

return text;

}

在同一個類裡建立一個main函式,測試WordReader,該main函式程式碼如下。

程式碼7.9

public static void main(String[] args) {

try{

String text = WordReader.readDoc("c:/test.doc");

System.out.println(text);

}catch(Exception e){

e.printStackTrace();

}

}

處理前的Doc檔案如圖7-15所示。


圖7-15 處理前的Word文件

使用程式碼處理後的文字如圖7-16所示。


圖7-16 處理後的結果

可以看到Word文件內的文字已經全部被提取了出來。

本文來自:http://book.csdn.net/bookfiles/312/10031212845.shtml 出自書籍:開發自己的搜尋引擎——Lucene 2.0+Heriterx(http://book.csdn.net/bookfiles/312/)