如何使用Apache POI将句子中的指定字符串设为粗体?
Apache POI Word替换带格式标记的字符串问题
我接触Apache POI仅4小时,需要实现三个需求:
- 在Word文档(.docx/XWPF)中定位不同字符串
- 将这些字符串替换为Map中的对应值
- 若字符串带有特殊标记(如
${key:bold}或${score:bold}),替换后的值需显示为粗体
最初尝试遍历段落而非Runs但未成功,当前方案来自Stack Overflow帖子,前两步已实现,但第三步遇到问题:尝试新建XWPFRun写入粗体字符串时,无法与现有代码兼容。
现有可运行代码(实现前两步)
ParagraphFieldReplacer.java
import java.util.List; import java.util.Map; import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; public final class ParagraphFieldReplacer { public static void replace(final Student student, final XWPFDocument document, final Map<String, FormatObject> fields) { final List<XWPFParagraph> paragraphs = document.getParagraphs(); for (final XWPFParagraph paragraph : paragraphs) replaceParagraph(student, paragraph, fields); } private static void replaceParagraph(final Student student, final XWPFParagraph paragraph, final Map<String, FormatObject> fields) throws POIXMLException { List<XWPFRun> runs; String paragraphtext; String runsText; XWPFRun nextRun; String target; XWPFRun run; for (final String key : fields.keySet()) { paragraphtext = paragraph.getText(); if (!(paragraphtext.contains("${"))) return; target = "${" + key + "}"; if (!(paragraphtext.contains(target))) continue; runs = paragraph.getRuns(); for (int i = 0; i < runs.size(); i++) { run = runs.get(i); runsText = run.getText(0); if (runsText.contains("${") || (runsText.contains("$") && runs.get(i + 1).getText(0).substring(0, 1).equals("{"))) { while (!(openTagCountIsEqualCloseTagCount(runsText))) { nextRun = runs.get(i + 1); runsText = runsText + nextRun.getText(0); paragraph.removeRun(i + 1); } if (!(runsText.contains(target))) run.setText(runsText, 0); else { final FormatObject format = fields.get(key); final String handle = format.handle(student); run.setText(runsText.replace(target, handle), 0); } } } } } private static boolean openTagCountIsEqualCloseTagCount(final String runText) { final int openTagCount = (runText.split("\\$\\{", -1).length - 1); final int closeTagCount = (runText.split("}", -1).length - 1); return (openTagCount == closeTagCount); } }
FormatObject.java
public final class FormatObject { private boolean bold; private FormatHandler<Student, String> handler; public FormatObject(final FormatHandler<Student, String> handler, final boolean bold) { this.handler= handler; this.bold = bold; } public boolean isBold() { return bold; } public String handle(final ZertifikationsStudent student) { return handler.handle(student); } }
FormatHandler.java
@FunctionalInterface public interface FormatHandler<P, R> { public R handle(P p); }
解决方案
要实现带粗体的替换,核心是拆分原Run文本,为替换内容单独创建带格式的Run,同时保留原有文本的格式一致性。修改replaceParagraph方法中的替换逻辑即可:
修改替换逻辑代码
将原代码中替换部分的代码替换为以下内容:
else { final FormatObject format = fields.get(key); final String handle = format.handle(student); // 拆分原文本为:标记前内容、目标标记、标记后内容 int startIndex = runsText.indexOf(target); int endIndex = startIndex + target.length(); String prefix = runsText.substring(0, startIndex); String suffix = runsText.substring(endIndex); // 设置标记前的文本,保留原Run格式 run.setText(prefix, 0); // 创建新Run放置替换后的内容,设置粗体 XWPFRun boldRun = paragraph.insertNewRun(i + 1); boldRun.setText(handle); boldRun.setBold(format.isBold()); // 复制原Run的字体、字号,保证样式统一 boldRun.setFontFamily(run.getFontFamily()); boldRun.setFontSize(run.getFontSize()); // 若有标记后内容,创建新Run放置,保留原格式 if (!suffix.isEmpty()) { XWPFRun suffixRun = paragraph.insertNewRun(i + 2); suffixRun.setText(suffix); suffixRun.setFontFamily(run.getFontFamily()); suffixRun.setFontSize(run.getFontSize()); suffixRun.setBold(run.isBold()); } // 移除原来的Run(内容已拆分到多个新Run中) paragraph.removeRun(i); // 调整索引,避免跳过后续内容 i--; }
额外说明
- 标记解析:如果你的原始标记是
${key:bold}格式,需要先通过正则提取key和格式参数,例如用Pattern.compile("\\$\\{(\\w+):bold\\}")匹配,再将对应FormatObject的bold属性设为true。 - 格式继承:新创建的Run务必复制原Run的字体、字号等属性,避免替换后的内容样式突兀。
- 索引调整:移除原Run并插入新Run后,要将循环索引
i递减,防止遗漏处理后续的Run。
内容的提问来源于stack exchange,提问作者user16028444
相关产品推荐
相关产品推荐

