使用POI讀寫Word doc文件

使用POI讀寫word doc文件

目錄

1     word doc文件

1.1     通過WordExtractor讀文件

1.2     通過HWPFDocument讀文件

2     word doc文件

 

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

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

l  Sectionword文檔的一個小節,一個word文檔可以由多個小節構成。

l  Paragraphword文檔的一個段落,一個小節可以由多個段落構成。

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

l  Table:一個表格。

l  TableRow:表格對應的行。

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

       SectionParagraphCharacterRunTable都繼承自Range

1       word doc文件

       在日常應用中,我們從word文件裏面讀取信息的情況非常少見,更多的還是把內容寫入到word文件中。使用POIword doc文件讀取數據時主要有兩種方式:通過WordExtractor讀和通過HWPFDocument讀。在WordExtractor內部進行信息讀取時還是通過HWPFDocument來獲取的。

 

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,而不是磁盤上的文件。如果要使這些修改生效的話,我們可以調用HWPFDocumentwrite方法把修改後的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文件

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

       在實際應用中,我們在生成word文件的時候都是生成某一類文件,該類文件的格式是固定的,只是某些字段不一樣罷了。所以在實際應用中,我們大可不必將整個word文件的內容都通過HWPFDocument生成。而是先在磁盤上新建一個word文檔,其內容就是我們需要生成的word文件的內容,然後把裏面一些屬於變量的內容使用類似於“${paramName}”這樣的方式代替。這樣我們在基於某些信息生成word文件的時候,只需要獲取基於該word文件的HWPFDocument,然後調用RangereplaceText()方法把對應的變量替換爲對應的值即可,之後再把當前的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所寫)

  • 5bc320e4-294f-3fc0-8886-e0b8eb02be44-thumb.png
  • 大小: 4.5 KB
  • 1a1bef9a-f0c1-3706-a130-d4e645cb62c8-thumb.png
  • 大小: 6.8 KB
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章