使用POI讀寫word doc文件

Apache poi的hwpf模塊是專門用來對word doc文件進行讀寫操做的。在hwpf裏面咱們使用HWPFDocument來表示一個word doc文檔。在HWPFDocument裏面有這麼幾個概念:java

l  Range:它表示一個範圍,這個範圍能夠是整個文檔,也能夠是裏面的某一小節(Section),也能夠是某一個段落(Paragraph),還能夠是擁有共同屬性的一段文本(CharacterRun)。app

l  Section:word文檔的一個小節,一個word文檔能夠由多個小節構成。this

l  Paragraph:word文檔的一個段落,一個小節能夠由多個段落構成。spa

l  CharacterRun:具備相同屬性的一段文本,一個段落能夠由多個CharacterRun組成。code

l  Table:一個表格。orm

l  TableRow:表格對應的行。繼承

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

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

1       讀word doc文件

       在平常應用中,咱們從word文件裏面讀取信息的狀況很是少見,更多的仍是把內容寫入到word文件中。使用POI從word doc文件讀取數據時主要有兩種方式:經過WordExtractor讀和經過HWPFDocument讀。在WordExtractor內部進行信息讀取時仍是經過HWPFDocument來獲取的。get

1.1     經過WordExtractor讀文件

       在使用WordExtractor讀文件時咱們只能讀到文件的文本內容和基於文檔的一些屬性,至於文檔內容的屬性等是沒法讀到的。若是要讀到文檔內容的屬性則須要使用HWPFDocument來讀取了。下面是使用WordExtractor讀取文件的一個示例:

public class HwpfTest {  
   
   @SuppressWarnings("deprecation")  
   @Test  
   public void testReadByExtractor() throws Exception {  
      InputStream is = new FileInputStream("D:\\test.doc");  
      WordExtractor extractor = new WordExtractor(is);  
      //輸出word文檔全部的文本  
      System.out.println(extractor.getText());  
      System.out.println(extractor.getTextFromPieces());  
      //輸出頁眉的內容  
      System.out.println("頁眉:" + extractor.getHeaderText());  
      //輸出頁腳的內容  
      System.out.println("頁腳:" + extractor.getFooterText());  
      //輸出當前word文檔的元數據信息,包括做者、文檔的修改時間等。  
      System.out.println(extractor.getMetadataTextExtractor().getText());  
      //獲取各個段落的文本  
      String paraTexts[] = extractor.getParagraphText();  
      for (int i=0; i<paraTexts.length; i++) {  
         System.out.println("Paragraph " + (i+1) + " : " + paraTexts[i]);  
      }  
      //輸出當前word的一些信息  
      printInfo(extractor.getSummaryInformation());  
      //輸出當前word的一些信息  
      this.printInfo(extractor.getDocSummaryInformation());  
      this.closeStream(is);  
   }  
    
   /** 
    * 輸出SummaryInfomation 
    * @param info 
    */  
   private void printInfo(SummaryInformation info) {  
      //做者  
      System.out.println(info.getAuthor());  
      //字符統計  
      System.out.println(info.getCharCount());  
      //頁數  
      System.out.println(info.getPageCount());  
      //標題  
      System.out.println(info.getTitle());  
      //主題  
      System.out.println(info.getSubject());  
   }  
    
   /** 
    * 輸出DocumentSummaryInfomation 
    * @param info 
    */  
   private void printInfo(DocumentSummaryInformation info) {  
      //分類  
      System.out.println(info.getCategory());  
      //公司  
      System.out.println(info.getCompany());  
   }  
    
   /** 
    * 關閉輸入流 
    * @param is 
    */  
   private void closeStream(InputStream is) {  
      if (is != null) {  
         try {  
            is.close();  
         } catch (IOException e) {  
            e.printStackTrace();  
         }  
      }  
   }  
    
}

1.2     經過HWPFDocument讀文件

       HWPFDocument是當前Word文檔的表明,它的功能比WordExtractor要強。經過它咱們能夠讀取文檔中的表格、列表等,還能夠對文檔的內容進行新增、修改和刪除操做。只是在進行完這些新增、修改和刪除後相關信息是保存在HWPFDocument中的,也就是說咱們改變的是HWPFDocument,而不是磁盤上的文件。若是要使這些修改生效的話,咱們能夠調用HWPFDocument的write方法把修改後的HWPFDocument輸出到指定的輸出流中。這能夠是原文件的輸出流,也能夠是新文件的輸出流(至關於另存爲)或其它輸出流。下面是一個經過HWPFDocument讀文件的示例:

public class HwpfTest {  
    
   @Test  
   public void testReadByDoc() throws Exception {  
      InputStream is = new FileInputStream("D:\\test.doc");  
      HWPFDocument doc = new HWPFDocument(is);  
      //輸出書籤信息  
      this.printInfo(doc.getBookmarks());  
      //輸出文本  
      System.out.println(doc.getDocumentText());  
      Range range = doc.getRange();  
//    this.insertInfo(range);  
      this.printInfo(range);  
      //讀表格  
      this.readTable(range);  
      //讀列表  
      this.readList(range);  
      //刪除range  
      Range r = new Range(2, 5, doc);  
      r.delete();//在內存中進行刪除,若是須要保存到文件中須要再把它寫回文件  
      //把當前HWPFDocument寫到輸出流中  
      doc.write(new FileOutputStream("D:\\test.doc"));  
      this.closeStream(is);  
   }  
    
   /** 
    * 關閉輸入流 
    * @param is 
    */  
   private void closeStream(InputStream is) {  
      if (is != null) {  
         try {  
            is.close();  
         } catch (IOException e) {  
            e.printStackTrace();  
         }  
      }  
   }  
    
   /** 
    * 輸出書籤信息 
    * @param bookmarks 
    */  
   private void printInfo(Bookmarks bookmarks) {  
      int count = bookmarks.getBookmarksCount();  
      System.out.println("書籤數量:" + count);  
      Bookmark bookmark;  
      for (int i=0; i<count; i++) {  
         bookmark = bookmarks.getBookmark(i);  
         System.out.println("書籤" + (i+1) + "的名稱是:" + bookmark.getName());  
         System.out.println("開始位置:" + bookmark.getStart());  
         System.out.println("結束位置:" + bookmark.getEnd());  
      }  
   }  
    
   /** 
    * 讀表格 
    * 每個回車符表明一個段落,因此對於表格而言,每個單元格至少包含一個段落,每行結束都是一個段落。 
    * @param range 
    */  
   private void readTable(Range range) {  
      //遍歷range範圍內的table。  
      TableIterator tableIter = new TableIterator(range);  
      Table table;  
      TableRow row;  
      TableCell cell;  
      while (tableIter.hasNext()) {  
         table = tableIter.next();  
         int rowNum = table.numRows();  
         for (int j=0; j<rowNum; j++) {  
            row = table.getRow(j);  
            int cellNum = row.numCells();  
            for (int k=0; k<cellNum; k++) {  
                cell = row.getCell(k);  
                //輸出單元格的文本  
                System.out.println(cell.text().trim());  
            }  
         }  
      }  
   }  
    
   /** 
    * 讀列表 
    * @param range 
    */  
   private void readList(Range range) {  
      int num = range.numParagraphs();  
      Paragraph para;  
      for (int i=0; i<num; i++) {  
         para = range.getParagraph(i);  
         if (para.isInList()) {  
            System.out.println("list: " + para.text());  
         }  
      }  
   }  
    
   /** 
    * 輸出Range 
    * @param range 
    */  
   private void printInfo(Range range) {  
      //獲取段落數  
      int paraNum = range.numParagraphs();  
      System.out.println(paraNum);  
      for (int i=0; i<paraNum; i++) {  
//       this.insertInfo(range.getParagraph(i));  
         System.out.println("段落" + (i+1) + ":" + range.getParagraph(i).text());  
         if (i == (paraNum-1)) {  
            this.insertInfo(range.getParagraph(i));  
         }  
      }  
      int secNum = range.numSections();  
      System.out.println(secNum);  
      Section section;  
      for (int i=0; i<secNum; i++) {  
         section = range.getSection(i);  
         System.out.println(section.getMarginLeft());  
         System.out.println(section.getMarginRight());  
         System.out.println(section.getMarginTop());  
         System.out.println(section.getMarginBottom());  
         System.out.println(section.getPageHeight());  
         System.out.println(section.text());  
      }  
   }  
    
   /** 
    * 插入內容到Range,這裏只會寫到內存中 
    * @param range 
    */  
   private void insertInfo(Range range) {  
      range.insertAfter("Hello");  
   }  
    
}

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輸出到對應的輸出流便可。下面是對應的代碼。

public class HwpfTest {  
    
   @Test  
   public void testWrite() throws Exception {  
      String templatePath = "D:\\word\\template.doc";  
      InputStream is = new FileInputStream(templatePath);  
      HWPFDocument doc = new HWPFDocument(is);  
      Range range = doc.getRange();  
      //把range範圍內的${reportDate}替換爲當前的日期  
      range.replaceText("${reportDate}", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));  
      range.replaceText("${appleAmt}", "100.00");  
      range.replaceText("${bananaAmt}", "200.00");  
      range.replaceText("${totalAmt}", "300.00");  
      OutputStream os = new FileOutputStream("D:\\word\\write.doc");  
      //把doc輸出到輸出流中  
      doc.write(os);  
      this.closeStream(os);  
      this.closeStream(is);  
   }  
    
   /** 
    * 關閉輸入流 
    * @param is 
    */  
   private void closeStream(InputStream is) {  
      if (is != null) {  
         try {  
            is.close();  
         } catch (IOException e) {  
            e.printStackTrace();  
         }  
      }  
   }  
   
   /** 
    * 關閉輸出流 
    * @param os 
    */  
   private void closeStream(OutputStream os) {  
      if (os != null) {  
         try {  
            os.close();  
         } catch (IOException e) {  
            e.printStackTrace();  
         }  
      }  
   }  
    
   
}

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

相關文章
相關標籤/搜索