使用POI來處理Excel和Word文件格式
7.3 使用POI來處理Excel和Word文件格式
Microsoft的Office系列產(chǎn)品擁有大量的用戶,Word、Excel也成為辦公文件的首選。在Java中,已經(jīng)有很多對于Word、Excel的開源的解決方案,其中比較出色的是Apache的Jakata項目的POI子項目。該項目的官方網(wǎng)站是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項目中最早的最基礎(chǔ)的一個模塊,是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文件的接口,支持讀寫功能,但目前該模塊還處于剛開始開發(fā)階段,只能實現(xiàn)一些簡單文件的操作,在后續(xù)版本中,會提供更強大的支持 |
HPSF(Horrible Property Set Format) |
HPSF 是Java到OLE 2 Compound Document Format文件的屬性設(shè)置的接口,屬性設(shè)置通常用來設(shè)置文檔的屬性(標題,作者,最后修改日期等),還可以設(shè)置用戶定義的屬性。HPSF支持讀寫功能,當前發(fā)布版本中直支持讀功能。 |
7.3.1 對Excel的處理類
下面通過HSSF提供的接口對Excel文件經(jīng)行處理。首先需要下載POI的包,可以到apache的官方網(wǎng)站下載,地址為:http://apache.justdn.org/jakarta/poi/,本書采用的是poi-2.5.1-final-20040804.jar,讀者可以下載當前的穩(wěn)定版本。把下載的包按照前面介紹的方式加入Build Path,然后新建一個ch7.poi包,并創(chuàng)建一個ExcelReader類。
ExcelReader類可以讀取一個XLS文件,然后將其內(nèi)容逐行提取出來,寫入文本文件。其代碼如下。
代碼7.6
public class ExcelReader {
// 創(chuàng)建文件輸入流
private BufferedReader reader = null;
// 文件類型
private String filetype;
// 文件二進制輸入流
private InputStream is = null;
// 當前的Sheet
private int currSheet;
// 當前位置
private int currPosition;
// Sheet數(shù)量
private int numOfSheets;
// HSSFWorkbook
HSSFWorkbook workbook = null;
// 設(shè)置Cell之間以空格分割
private static String EXCEL_LINE_DELIMITER = " ";
// 設(shè)置最大列數(shù)
private static int MAX_EXCEL_COLUMNS = 64;
// 構(gòu)造函數(shù)創(chuàng)建一個ExcelReader
public ExcelReader(String inputfile) throws IOException, Exception {
// 判斷參數(shù)是否為空或沒有意義
if (inputfile == null || inputfile.trim().equals("")) {
throw new IOException("no input file specified");
}
// 取得文件名的后綴名賦值給filetype
this.filetype = inputfile.substring(inputfile.lastIndexOf(".") + 1);
// 設(shè)置開始行為0
currPosition = 0;
// 設(shè)置當前位置為0
currSheet = 0;
// 創(chuàng)建文件輸入流
is = new FileInputStream(inputfile);
// 判斷文件格式
if (filetype.equalsIgnoreCase("txt")) {
// 如果是txt則直接創(chuàng)建BufferedReader讀取
reader = new BufferedReader(new InputStreamReader(is));
}
else if (filetype.equalsIgnoreCase("xls")) {
// 如果是Excel文件則創(chuàng)建HSSFWorkbook讀取
workbook = new HSSFWorkbook(is);
// 設(shè)置Sheet數(shù)
numOfSheets = workbook.getNumberOfSheets();
}
else {
throw new Exception("File Type Not Supported");
}
}
// 函數(shù)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")) {
// 根據(jù)currSheet值獲得當前的sheet
HSSFSheet sheet = workbook.getSheetAt(currSheet);
// 判斷當前行是否到但前Sheet的結(jié)尾
if (currPosition > sheet.getLastRowNum()) {
// 當前行位置清零
currPosition = 0;
// 判斷是否還有Sheet
while (currSheet != numOfSheets - 1) {
// 得到下一張Sheet
sheet = workbook.getSheetAt(currSheet + 1);
// 當前行數(shù)是否已經(jīng)到達文件末尾
if (currPosition == sheet.getLastRowNum()) {
// 當前Sheet指向下一張Sheet
currSheet++;
continue;
} else {
// 獲取當前行數(shù)
int row = currPosition;
currPosition++;
// 讀取當前行數(shù)據(jù)
return getLine(sheet, row);
}
}
return null;
}
// 獲取當前行數(shù)
int row = currPosition;
currPosition++;
// 讀取當前行數(shù)據(jù)
return getLine(sheet, row);
}
return null;
}
// 函數(shù)getLine返回Sheet的一行數(shù)據(jù)
private String getLine(HSSFSheet sheet, int row) {
// 根據(jù)行數(shù)取得Sheet的一行
HSSFRow rowline = sheet.getRow(row);
// 創(chuàng)建字符創(chuàng)緩沖區(qū)
StringBuffer buffer = new StringBuffer();
// 獲取當前行的列數(shù)
int filledColumns = rowline.getLastCellNum();
HSSFCell cell = null;
// 循環(huán)遍歷所有列
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轉(zhuǎn)換成本地格式的字符串
cellvalue = cell.getDateCellValue().toLocaleString();
}
// 如果是純數(shù)字
else {
// 取得當前Cell的數(shù)值
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);
}
// 以字符串返回該行的數(shù)據(jù)
return buffer.toString();
}
// close函數(shù)執(zhí)行流的關(guān)閉操作
public void close() {
// 如果is不為空,則關(guān)閉InputSteam文件輸入流
if (is != null) {
try {
is.close();
} catch (IOException e) {
is = null;
}
}
// 如果reader不為空則關(guān)閉BufferedReader文件輸入流
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
reader = null;
}
}
}
}
7.3.2 ExcelReader的運行效果
下面創(chuàng)建一個main函數(shù),用來測試上面的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函數(shù)先創(chuàng)建一個ExcelReader類,然后調(diào)用它提供的接口readLine,對XLS文件進行讀取,打印到控制臺,處理前的XLS文件如圖7-12所示。
圖7-12 處理前的XLS文件內(nèi)容
運行main函數(shù)進行內(nèi)容提取后,Eclipse的控制臺輸出如圖7-13所示。
圖7-13 輸出結(jié)果
可以看到,Excel文件中的內(nèi)容已經(jīng)被成功的輸出了出來。
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 |
數(shù)值型 |
本例采用了CELL_TYPE_STRING和CELL_TYPE_NUMERIC類型,因為在Excel文件中只有字符串和數(shù)字。如果Cell的Type為CELL_TYPE_NUMERIC時,還需要進一步判斷該Cell的數(shù)據(jù)格式,因為它有可能是Date類型,在Excel中的Date類型也是以Double類型的數(shù)字存儲的。Excel中的Date表示當前時間與1900年1月1日相隔的天數(shù),所以需要調(diào)用HSSFDateUtil的isCellDateFormatted方法,判斷該Cell的數(shù)據(jù)格式是否是Excel Date類型。如果是,則調(diào)用getDateCellValue方法,返回一個Java類型的Date。
實際上Excel的數(shù)據(jù)格式有很多,還支持用戶自定義的類型,在Excel中,選擇一個單元格然后右鍵選擇“設(shè)置單元格格式”,在彈出的單元格格式中選中“數(shù)字”,如圖7-14所示。
圖7-14 Excel的單元格格式
圖中的數(shù)據(jù)有數(shù)值、貨幣、時間、日期、文本等格式。這些數(shù)據(jù)格式在POI中的HSSFDataFormat類里都有相應(yīng)的定義。
HSSFDataFormat是HSSF子項目里面定義的一個類。類HSSFDataFormat允許用戶新建數(shù)據(jù)格式類型。HSSFDataFormat類包含靜態(tài)方法static java.lang.String getBuiltinFormat(short index),它可以根據(jù)編號返回內(nèi)置數(shù)據(jù)類型。另外static short getBuiltinFormat(java.lang.String format)方法則可以根據(jù)數(shù)據(jù)類型返回其編號,static java.util.List getBuiltinFormats()可以返回整個內(nèi)置的數(shù)據(jù)格式列表。
在HSSFDataFormat里一共定義了49種內(nèi)置的數(shù)據(jù)格式,如表7-4所示。
表7-4 HSSFDataFormat的數(shù)據(jù)格式
內(nèi)置數(shù)據(jù)類型 |
編號 |
"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 |
在上面表中,字符串類型所對應(yīng)的是數(shù)據(jù)格式為"@"(最后一行),也就是HSSFDataFormat中定義的值為0x31(49)的那行。Date類型的值的范圍是0xe-0x11,本例子中的Date格式為""m/d/yy"",在HSSFDataFormat定義的值為0xe(14)。
需要注意的一點是,所創(chuàng)建的Excel必須是在Microsoft Excel 97到Excel XP的版本上的,如果在Excel 2003中創(chuàng)建文件后,在使用POI進行解析時,可能會出現(xiàn)問題。它會把Date類型當作自定義類型。POI目前只提供對Microsoft Excel XP以下的版本的支持,在以后的版本中,希望會提供對Microsoft Excel 2003更好的支持。
7.3.4 對Word的處理類
除了支持對Excel文件的讀取外,POI還提供對Word的DOC格式文件的讀取。但在它的發(fā)行版本中沒有發(fā)布對Word支持的模塊,需要另外下載一個POI的擴展的Jar包。用戶可以到http://www.ibiblio.org/maven2/org/textmining/tm-extractors/0.4/下載,本書采用的是tm-extractors-0.4_zip。
下載后,把該包加入工程的Build Path中,然后在ch7.poi包下新建一個類WordReader,該類提供一個靜態(tài)方法readDoc,讀取一個DOC文件并返回文本。函數(shù)內(nèi)容很簡單,就是調(diào)用WordExtractor的API來提取DOC的內(nèi)容到字符串,該函數(shù)的代碼如下。
代碼7.8
public static String readDoc(String doc) throws Exception {
// 創(chuàng)建輸入流讀取DOC文件
FileInputStream in = new FileInputStream(new File(doc));
WordExtractor extractor = null;
String text = null;
// 創(chuàng)建WordExtractor
extractor = new WordExtractor();
// 對DOC文件進行提取
text = extractor.extractText(in);
return text;
}
在同一個類里創(chuàng)建一個main函數(shù),測試WordReader,該main函數(shù)代碼如下。
代碼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 處理后的結(jié)果
可以看到Word文檔內(nèi)的文本已經(jīng)全部被提取了出來。
posted on 2007-11-01 20:56 sooxin 閱讀(733) 評論(1) 編輯 收藏 所屬分類: JAVA