1. 程式人生 > >使用POI讀寫Word

使用POI讀寫Word

1     word doc檔案

1.1     通過WordExtractor讀檔案

1.2     通過HWPFDocument讀檔案

2     word doc檔案

       Apache poi的hwpf模組是專門用來對word doc檔案進行讀寫操作的。在hwpf裡面我們使用HWPFDocument來表示一個word doc文件。在HWPFDocument裡面有這麼幾個概念:

l  Range:它表示一個範圍,這個範圍可以是整個文件,也可以是裡面的某一小節(Section),也可以是某一個段落(Paragraph),還可以是擁有共同屬性的一段文字(CharacterRun)。

l  Section:word文件的一個小節,一個word文件可以由多個小節構成。

l  Paragraph:word文件的一個段落,一個小節可以由多個段落構成。

l  CharacterRun:具有相同屬性的一段文字,一個段落可以由多個CharacterRun組成。

l  Table:一個表格。

l  TableRow:表格對應的行。

l  TableCell:表格對應的單元格。

       Section、Paragraph、CharacterRun和Table都繼承自Range。

1       讀word doc檔案

       在日常應用中,我們從word檔案裡面讀取資訊的情況非常少見,更多的還是把內容寫入到word檔案中。使用POI從word doc檔案讀取資料時主要有兩種方式:通過WordExtractor

讀和通過HWPFDocument讀。在WordExtractor內部進行資訊讀取時還是通過HWPFDocument來獲取的。

1.1     通過WordExtractor讀檔案

       在使用WordExtractor讀檔案時我們只能讀到檔案的文字內容和基於文件的一些屬性,至於文件內容的屬性等是無法讀到的。如果要讀到文件內容的屬性則需要使用HWPFDocument來讀取了。下面是使用WordExtractor讀取檔案的一個示例:

Java程式碼  收藏程式碼
  1. public class HwpfTest {  
  2.    @SuppressWarnings("deprecation")  
  3.    @Test
      
  4.    public void testReadByExtractor() throws Exception {  
  5.       InputStream is = new FileInputStream("D:\\test.doc");  
  6.       WordExtractor extractor = new WordExtractor(is);  
  7.       //輸出word文件所有的文字  
  8.       System.out.println(extractor.getText());  
  9.       System.out.println(extractor.getTextFromPieces());  
  10.       //輸出頁首的內容  
  11.       System.out.println("頁首:" + extractor.getHeaderText());  
  12.       //輸出頁尾的內容  
  13.       System.out.println("頁尾:" + extractor.getFooterText());  
  14.       //輸出當前word文件的元資料資訊,包括作者、文件的修改時間等。  
  15.       System.out.println(extractor.getMetadataTextExtractor().getText());  
  16.       //獲取各個段落的文字  
  17.       String paraTexts[] = extractor.getParagraphText();  
  18.       for (int i=0; i<paraTexts.length; i++) {  
  19.          System.out.println("Paragraph " + (i+1) + " : " + paraTexts[i]);  
  20.       }  
  21.       //輸出當前word的一些資訊  
  22.       printInfo(extractor.getSummaryInformation());  
  23.       //輸出當前word的一些資訊  
  24.       this.printInfo(extractor.getDocSummaryInformation());  
  25.       this.closeStream(is);  
  26.    }  
  27.    /** 
  28.     * 輸出SummaryInfomation 
  29.     * @param info 
  30.     */  
  31.    private void printInfo(SummaryInformation info) {  
  32.       //作者  
  33.       System.out.println(info.getAuthor());  
  34.       //字元統計  
  35.       System.out.println(info.getCharCount());  
  36.       //頁數  
  37.       System.out.println(info.getPageCount());  
  38.       //標題  
  39.       System.out.println(info.getTitle());  
  40.       //主題  
  41.       System.out.println(info.getSubject());  
  42.    }  
  43.    /** 
  44.     * 輸出DocumentSummaryInfomation 
  45.     * @param info 
  46.     */  
  47.    private void printInfo(DocumentSummaryInformation info) {  
  48.       //分類  
  49.       System.out.println(info.getCategory());  
  50.       //公司  
  51.       System.out.println(info.getCompany());  
  52.    }  
  53.    /** 
  54.     * 關閉輸入流 
  55.     * @param is 
  56.     */  
  57.    private void closeStream(InputStream is) {  
  58.       if (is != null) {  
  59.          try {  
  60.             is.close();  
  61.          } catch (IOException e) {  
  62.             e.printStackTrace();  
  63.          }  
  64.       }  
  65.    }  
  66. }  

1.2     通過HWPFDocument讀檔案

       HWPFDocument是當前Word文件的代表,它的功能比WordExtractor要強。通過它我們可以讀取文件中的表格、列表等,還可以對文件的內容進行新增、修改和刪除操作。只是在進行完這些新增、修改和刪除後相關資訊是儲存在HWPFDocument中的,也就是說我們改變的是HWPFDocument,而不是磁碟上的檔案。如果要使這些修改生效的話,我們可以呼叫HWPFDocument的write方法把修改後的HWPFDocument輸出到指定的輸出流中。這可以是原檔案的輸出流,也可以是新檔案的輸出流(相當於另存為)或其它輸出流。下面是一個通過HWPFDocument讀檔案的示例:

Java程式碼  收藏程式碼
  1. public class HwpfTest {  
  2.    @Test  
  3.    public void testReadByDoc() throws Exception {  
  4.       InputStream is = new FileInputStream("D:\\test.doc");  
  5.       HWPFDocument doc = new HWPFDocument(is);  
  6.       //輸出書籤資訊  
  7.       this.printInfo(doc.getBookmarks());  
  8.       //輸出文字  
  9.       System.out.println(doc.getDocumentText());  
  10.       Range range = doc.getRange();  
  11. //    this.insertInfo(range);  
  12.       this.printInfo(range);  
  13.       //讀表格  
  14.       this.readTable(range);  
  15.       //讀列表  
  16.       this.readList(range);  
  17.       //刪除range  
  18.       Range r = new Range(25, doc);  
  19.       r.delete();//在記憶體中進行刪除,如果需要儲存到檔案中需要再把它寫回檔案  
  20.       //把當前HWPFDocument寫到輸出流中  
  21.       doc.write(new FileOutputStream("D:\\test.doc"));  
  22.       this.closeStream(is);  
  23.    }  
  24.    /** 
  25.     * 關閉輸入流 
  26.     * @param is 
  27.     */  
  28.    private void closeStream(InputStream is) {  
  29.       if (is != null) {  
  30.          try {  
  31.             is.close();  
  32.          } catch (IOException e) {  
  33.             e.printStackTrace();  
  34.          }  
  35.       }  
  36.    }  
  37.    /** 
  38.     * 輸出書籤資訊 
  39.     * @param bookmarks 
  40.     */  
  41.    private void printInfo(Bookmarks bookmarks) {  
  42.       int count = bookmarks.getBookmarksCount();  
  43.       System.out.println("書籤數量:" + count);  
  44.       Bookmark bookmark;  
  45.       for (int i=0; i<count; i++) {  
  46.          bookmark = bookmarks.getBookmark(i);  
  47.          System.out.println("書籤" + (i+1) + "的名稱是:" + bookmark.getName());  
  48.          System.out.println("開始位置:" + bookmark.getStart());  
  49.          System.out.println("結束位置:" + bookmark.getEnd());  
  50.       }  
  51.    }  
  52.    /** 
  53.     * 讀表格 
  54.     * 每一個回車符代表一個段落,所以對於表格而言,每一個單元格至少包含一個段落,每行結束都是一個段落。 
  55.     * @param range 
  56.     */  
  57.    private void readTable(Range range) {  
  58.       //遍歷range範圍內的table。  
  59.       TableIterator tableIter = new TableIterator(range);  
  60.       Table table;  
  61.       TableRow row;  
  62.       TableCell cell;  
  63.       while (tableIter.hasNext()) {  
  64.          table = tableIter.next();  
  65.          int rowNum = table.numRows();  
  66.          for (int j=0; j<rowNum; j++) {  
  67.             row = table.getRow(j);  
  68.             int cellNum = row.numCells();  
  69.             for (int k=0; k<cellNum; k++) {  
  70.                 cell = row.getCell(k);  
  71.                 //輸出單元格的文字  
  72.                 System.out.println(cell.text().trim());  
  73.             }  
  74.          }  
  75.       }  
  76.    }  
  77.    /** 
  78.     * 讀列表 
  79.     * @param range 
  80.     */  
  81.    private void readList(Range range) {  
  82.       int num = range.numParagraphs();  
  83.       Paragraph para;  
  84.       for (int i=0; i<num; i++) {  
  85.          para = range.getParagraph(i);  
  86.          if (para.isInList()) {  
  87.             System.out.println("list: " + para.text());  
  88.          }  
  89.       }  
  90.    }  
  91.    /** 
  92.     * 輸出Range 
  93.     * @param range 
  94.     */  
  95.    private void printInfo(Range range) {  
  96.       //獲取段落數  
  97.       int paraNum = range.numParagraphs();  
  98.       System.out.println(paraNum);  
  99.       for (int i=0; i<paraNum; i++) {  
  100. //       this.insertInfo(range.getParagraph(i));  
  101.          System.out.println("段落" + (i+1) + ":" + range.getParagraph(i).text());  
  102.          if (i == (paraNum-1)) {  
  103.             this.insertInfo(range.getParagraph(i));  
  104.          }  
  105.       }  
  106.       int secNum = range.numSections();  
  107.       System.out.println(secNum);  
  108.       Section section;  
  109.       for (int i=0; i<secNum; i++) {  
  110.          section = range.getSection(i);  
  111.          System.out.println(section.getMarginLeft());  
  112.          System.out.println(section.getMarginRight());  
  113.          System.out.println(section.getMarginTop());  
  114.          System.out.println(section.getMarginBottom());  
  115.          System.out.println(section.getPageHeight());  
  116.          System.out.println(section.text());  
  117.       }  
  118.    }  
  119.    /** 
  120.     * 插入內容到Range,這裡只會寫到記憶體中 
  121.     * @param range 
  122.     */  
  123.    private void insertInfo(Range range) {  
  124.       range.insertAfter("Hello");  
  125.    }  
  126. }  

2       寫word doc檔案

       在使用POI寫word doc檔案的時候我們必須要先有一個doc檔案才行,因為我們在寫doc檔案的時候是通過HWPFDocument來寫的,而HWPFDocument是要依附於一個doc檔案的。所以通常的做法是我們先在硬碟上準備好一個內容空白的doc檔案,然後建立一個基於該空白檔案的HWPFDocument。之後我們就可以往HWPFDocument裡面新增內容了,然後再把它寫入到另外一個doc檔案中,這樣就相當於我們使用POI生成了word doc檔案。

       在實際應用中,我們在生成word檔案的時候都是生成某一類檔案,該類檔案的格式是固定的,只是某些欄位不一樣罷了。所以在實際應用中,我們大可不必將整個word檔案的內容都通過HWPFDocument生成。而是先在磁碟上新建一個word文件,其內容就是我們需要生成的word檔案的內容,然後把裡面一些屬於變數的內容使用類似於“${paramName}”這樣的方式代替。這樣我們在基於某些資訊生成word檔案的時候,只需要獲取基於該word檔案的HWPFDocument,然後呼叫Range的replaceText()方法把對應的變數替換為對應的值即可,之後再把當前的HWPFDocument寫入到新的輸出流中。這種方式在實際應用中用的比較多,因為它不但可以減少我們的工作量,還可以讓文字的格式更加的清晰。下面我們就來基於這種方式做一個示例。

       假設我們現在擁有一些變動的資訊,然後需要通過這些資訊生成如下格式的word doc檔案:



 

       那麼根據上面的描述,首先第一步,我們建立一個對應格式的doc檔案作為模板,其內容是這樣的:



 

       有了這樣一個模板之後,我們就可以建立對應的HWPFDocument,然後替換對應的變數為相應的值,再把HWPFDocument輸出到對應的輸出流即可。下面是對應的程式碼。

Java程式碼  收藏程式碼
  1. public class HwpfTest {  
  2.    @Test  
  3.    public void testWrite() throws Exception {  
  4.       String templatePath = "D:\\word\\template.doc";  
  5.       InputStream is = new FileInputStream(templatePath);  
  6.       HWPFDocument doc = new HWPFDocument(is);  
  7.       Range range = doc.getRange();  
  8.       //把range範圍內的${reportDate}替換為當前的日期  
  9.       range.replaceText("${reportDate}"new SimpleDateFormat("yyyy-MM-dd").format(new Date()));  
  10.       range.replaceText("${appleAmt}""100.00");  
  11.       range.replaceText("${bananaAmt}""200.00");  
  12.       range.replaceText("${totalAmt}""300.00");  
  13.       OutputStream os = new FileOutputStream("D:\\word\\write.doc");  
  14.       //把doc輸出到輸出流中  
  15.       doc.write(os);  
  16.       this.closeStream(os);  
  17.       this.closeStream(is);  
  18.    }  
  19.    /** 
  20.     * 關閉輸入流 
  21.     * @param is 
  22.     */  
  23.    private void closeStream(InputStream is) {  
  24.       if (is != null) {  
  25.          try {  
  26.             is.close();  
  27.          } catch (IOException e) {  
  28.             e.printStackTrace();  
  29.          }  
  30.       }  
  31.    }  
  32.    /** 
  33.     * 關閉輸出流 
  34.     * @param os 
  35.     */  
  36.    private void closeStream(OutputStream os) {  
  37.       if (os != null) {  
  38.          try {  
  39.             os.close();  
  40.          } catch (IOException e) {  
  41.             e.printStackTrace();  
  42.          }  
  43.       }  
  44.    }  
  45. }  

(注:本文是基於poi3.9所寫)

原文轉載自:http://haohaoxuexi.iteye.com/blog/2031335