首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 讀取 Word 文件完整指南:Apache POI 與 RTF 純文字兩大方案深度解析

     編輯:Java 相關知識

前言

Java 讀取 Word 文件(.doc/.docx)是企業級應用常見需求,如報表解析、合約審核、文檔轉換。主流方案分為兩大陣營:Apache POI(官方標準、功能完整、支援 .doc/.docx)與 RTF 純文字處理(無依賴、輕量、適合簡單模板替換)。本文深度解析兩種方案的適用場景、核心 API 與實戰代碼。

方案對比快速選型

維度Apache POIRTF 字串替換
支援格式.doc (HWPF) / .docx (XWPF).rtf 僅限
依賴大小~15MB (poi-ooxml)零依賴 (JDK 內建)
解析能力完整結構 (段落、表格、樣式、圖片)純文字+簡單格式標記
效能較慢、記憶體佔用大極快、記憶體佔用極小
適用場景複雜文檔解析、格式保真轉換固定模板填充、簡單報表生成

方案一:Apache POI 完整實作

Maven 依賴

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.3.0</version>
</dependency>

.docx 文件讀取 (XWPF)

import org.apache.poi.xwpf.usermodel.*;
import java.io.FileInputStream;
import java.util.List;

public class DocxReader {
    public static void readDocx(String filePath) throws Exception {
        try (FileInputStream fis = new FileInputStream(filePath);
             XWPFDocument doc = new XWPFDocument(fis)) {

            // 1. 讀取段落文字
            System.out.println("=== 段落內容 ===");
            for (XWPFParagraph para : doc.getParagraphs()) {
                System.out.println(para.getText());
            }

            // 2. 讀取表格
            System.out.println("\n=== 表格內容 ===");
            for (XWPFTable table : doc.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        System.out.print("[" + cell.getText() + "] ");
                    }
                    System.out.println();
                }
                System.out.println("--- 分隔 ---");
            }

            // 3. 讀取圖片關係 (進階)
            // List pictures = doc.getAllPictures();
        }
    }
}

.doc 文件讀取 (HWPF) - 舊版相容

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import java.io.FileInputStream;

public class DocReader {
    public static void readDoc(String filePath) throws Exception {
        try (FileInputStream fis = new FileInputStream(filePath);
             POIFSFileSystem fs = new POIFSFileSystem(fis);
             HWPFDocument doc = new HWPFDocument(fs)) {

            Range range = doc.getRange();
            
            // 讀取所有文字
            System.out.println("全文文字:");
            System.out.println(range.text());

            // 遍歷表格
            TableIterator it = new TableIterator(range);
            while (it.hasNext()) {
                Table table = (Table) it.next();
                for (int r = 0; r < table.numRows(); r++) {
                    TableRow row = table.getRow(r);
                    for (int c = 0; c < row.numCells(); c++) {
                        TableCell cell = row.getCell(c);
                        for (int p = 0; p < cell.numParagraphs(); p++) {
                            Paragraph para = cell.getParagraph(p);
                            System.out.print("[" + para.text() + "] ");
                        }
                    }
                    System.out.println();
                }
            }
        }
    }
}

方案二:RTF 模板字串替換 (零依賴)

RTF (Rich Text Format) 是純文字格式,控制碼為反斜線開頭 (如 \b 粗體、\par 換行)。核心思路:將動態內容轉為 RTF 編碼,替換模板佔位符。

核心編碼工具類

public class RtfEncoder {
    private static final char[] HEX = "0123456789ABCDEF".toCharArray();

    /** 將純文字轉為 RTF 十六進位編碼 (如 "中文" -> \'d6\'d0\'ce\'c4) */
    public static String encode(String text) {
        if (text == null) return "";
        StringBuilder sb = new StringBuilder();
        byte[] bytes = text.getBytes(StandardCharsets.UTF_8); // 或 Big5 視模板編碼
        for (byte b : bytes) {
            sb.append("\\'").append(HEX[(b >> 4) & 0xF]).append(HEX[b & 0xF]);
        }
        return sb.toString();
    }

    /** 讀取 RTF 模板檔案為字串 */
    public static String readTemplate(String path) throws IOException {
        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
    }

    /** 替換佔位符並輸出新檔案 */
    public static void generate(String template, Map placeholders, String outputPath) 
            throws IOException {
        String result = template;
        for (Map.Entry entry : placeholders.entrySet()) {
            String encoded = encode(entry.getValue());
            result = result.replace(entry.getKey(), encoded);
        }
        Files.write(Paths.get(outputPath), result.getBytes(StandardCharsets.UTF_8));
    }
}

實戰範例:合約模板生成

public class ContractGenerator {
    public static void main(String[] args) throws Exception {
        // 1. 讀取模板 (模板內含 ${partyA}、${partyB}、${amount} 等佔位符)
        String template = RtfEncoder.readTemplate("template/contract.rtf");

        // 2. 準備動態資料
        Map data = new HashMap<>();
        data.put("${partyA}", "甲方公司股份有限公司");
        data.put("${partyB}", "乙方科技有限公司");
        data.put("${amount}", "新臺幣壹佰萬元整 (NT$1,000,000)");
        data.put("${date}", "2024年12月25日");

        // 3. 生成合約
        RtfEncoder.generate(template, data, "output/contract_20241225.rtf");
        System.out.println("合約生成完成!");
    }
}

進階議題與避坑指南

1. 中文編碼陷阱

  • RTF 檔案常用 \ansicpg950 (Big5) 或 \ansicpg936 (GBK) 標記編碼
  • Java 讀取時需對應編碼:new String(bytes, "Big5")
  • POI 內部自動處理 .docx (UTF-8/XML),.doc 需注意代碼頁

2. 大檔案記憶體優化 (POI)

// XWPFDocument 預設載入整份文件到記憶體,大檔案易 OOM
// 方案:使用 SAX 事件驅動解析 (XWPFDocument 無直接支援,需自行解析 word/document.xml)
// 或拆分文件、增加 JVM Heap (-Xmx2g)

3. 表格跨頁、合併儲存格處理

  • POI: cell.getCTTc().getTcPr().getGridSpan() 取得橫向合併
  • 垂直合併需解析 vMerge 屬性

4. 圖片提取與轉換

// .docx 圖片提取
for (XWPFPictureData pic : doc.getAllPictures()) {
    String ext = pic.suggestFileExtension(); // png/jpeg/emf...
    byte[] data = pic.getData();
    Files.write(Paths.get("img_" + System.currentTimeMillis() + "." + ext), data);
}

總結建議

  • 需解析現有 .doc/.docx、提取結構化資料 → Apache POI (唯一選擇)
  • 固定版面模板填充、生成報表/合約、追求零依賴/高效能 → RTF 字串替換
  • 現代專案新開發 → 建議直接產出 HTML/PDF,避開 Word 格式複雜度
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved