如何用Java替换带混合格式的Docx文件中的文本?
处理带混合格式的Docx文本替换方案
你之前用的docx-word-replacer这类封装库没法处理混合格式文本替换,核心原因是带格式的文本在Docx的底层结构里会被拆分成多个Run(独立格式的文本块)——比如"appel"里某个字母加粗,就会被拆成2个甚至多个Run,封装库只会匹配单个Run里的完整文本,自然找不到目标内容。
用Apache POI的XWPF组件可以直接操作Docx的底层结构,实现跨Run的文本匹配与替换,以下是可行方案:
1. 引入依赖
在pom.xml中添加POI的相关依赖:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> </dependency>
2. 核心替换代码
以下代码会遍历文档的段落(如果需要处理表格、页眉页脚可自行扩展逻辑),拼接相邻Run的文本查找目标字符串,找到后替换并统一格式(允许更改格式的前提下):
import org.apache.poi.xwpf.usermodel.*; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; public class MixedFormatReplacer { public static void main(String[] args) throws Exception { String targetText = "appel"; String replaceText = "banana"; // 读取文档 XWPFDocument doc = new XWPFDocument(new FileInputStream("<你的Docx文件路径>")); // 处理所有段落 for (XWPFParagraph para : doc.getParagraphs()) { replaceMixedFormatText(para, targetText, replaceText); } // 处理表格(可选,如果文档有表格需要替换) for (XWPFTable table : doc.getTables()) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph para : cell.getParagraphs()) { replaceMixedFormatText(para, targetText, replaceText); } } } } // 保存修改后的文档 doc.write(new FileOutputStream("<保存的文件路径>")); doc.close(); } private static void replaceMixedFormatText(XWPFParagraph para, String target, String replacement) { List<XWPFRun> runs = para.getRuns(); if (runs.isEmpty()) return; // 拼接所有Run的文本,同时记录每个字符对应的Run索引 StringBuilder fullText = new StringBuilder(); List<Integer> charToRunIndex = new ArrayList<>(); for (int i = 0; i < runs.size(); i++) { String runText = runs.get(i).getText(0); if (runText == null) continue; fullText.append(runText); for (int j = 0; j < runText.length(); j++) { charToRunIndex.add(i); } } String text = fullText.toString(); int startIndex = text.indexOf(target); while (startIndex != -1) { int endIndex = startIndex + target.length(); // 获取目标文本覆盖的Run范围 int startRunIndex = charToRunIndex.get(startIndex); int endRunIndex = charToRunIndex.get(endIndex - 1); // 删除所有被覆盖的Run for (int i = endRunIndex; i >= startRunIndex; i--) { para.removeRun(i); } // 创建新的Run,使用第一个被替换Run的格式(或自定义格式) XWPFRun newRun = para.insertNewRun(startRunIndex); newRun.setText(replacement); // 可以自定义格式,比如设置加粗、字体等 newRun.setBold(runs.get(startRunIndex).isBold()); newRun.setFontSize(runs.get(startRunIndex).getFontSize()); newRun.setFontFamily(runs.get(startRunIndex).getFontFamily()); // 重新生成文本和Run映射,继续查找下一个匹配 fullText = new StringBuilder(); charToRunIndex.clear(); runs = para.getRuns(); for (int i = 0; i < runs.size(); i++) { String runText = runs.get(i).getText(0); if (runText == null) continue; fullText.append(runText); for (int j = 0; j < runText.length(); j++) { charToRunIndex.add(i); } } text = fullText.toString(); startIndex = text.indexOf(target); } } }
关键说明
- 代码会先把段落里所有Run的文本拼接起来,找到目标字符串的位置,再定位到对应的Run块
- 替换时会删除原来的目标Run,插入新的Run,你可以根据需求调整新Run的格式(比如统一改成加粗、换字体,或者保留原格式)
- 如果需要处理页眉、页脚,可以添加
doc.getHeaderList()和doc.getFooterList()的遍历逻辑,处理方式和段落一致
内容的提问来源于stack exchange,提问作者user10053673
相关产品推荐
相关产品推荐

