You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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--;
}

额外说明

  1. 标记解析:如果你的原始标记是${key:bold}格式,需要先通过正则提取key和格式参数,例如用Pattern.compile("\\$\\{(\\w+):bold\\}")匹配,再将对应FormatObject的bold属性设为true。
  2. 格式继承:新创建的Run务必复制原Run的字体、字号等属性,避免替换后的内容样式突兀。
  3. 索引调整:移除原Run并插入新Run后,要将循环索引i递减,防止遗漏处理后续的Run。

内容的提问来源于stack exchange,提问作者user16028444

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:05:27