替换Apache POI XWPF中的文本

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了替换Apache POI XWPF中的文本相关的知识,希望对你有一定的参考价值。

我刚刚发现Apache POI库对于使用Java编辑Word文件非常有用。具体来说,我想使用Apache POI的XWPF类编辑DOCX文件。我发现没有适当的方法/文档,我可以这样做。有人可以分步说明,如何替换DOCX文件中的一些文本。

**文本可以在行/段落或表格行/列中

提前致谢 :)

答案

你需要的方法是XWPFRun.setText(String)。只需按照您的方式浏览文件,直到找到感兴趣的XWPFRun,找出您想要的新文本并替换它。 (运行是具有相同格式的文本序列)

你应该可以这样做:

XWPFDocument doc = new XWPFDocument(OPCPackage.open("input.docx"));
for (XWPFParagraph p : doc.getParagraphs()) {
    List<XWPFRun> runs = p.getRuns();
    if (runs != null) {
        for (XWPFRun r : runs) {
            String text = r.getText(0);
            if (text != null && text.contains("needle")) {
                text = text.replace("needle", "haystack");
                r.setText(text, 0);
            }
        }
    }
}
for (XWPFTable tbl : doc.getTables()) {
   for (XWPFTableRow row : tbl.getRows()) {
      for (XWPFTableCell cell : row.getTableCells()) {
         for (XWPFParagraph p : cell.getParagraphs()) {
            for (XWPFRun r : p.getRuns()) {
              String text = r.getText(0);
              if (text != null && text.contains("needle")) {
                text = text.replace("needle", "haystack");
                r.setText(text,0);
              }
            }
         }
      }
   }
}
doc.write(new FileOutputStream("output.docx"));
另一答案

以下是我们使用Apache POI替换文本所做的工作。我们发现替换整个XWPFParagraph的文本而不是运行是不值得的麻烦和简单。由于Microsoft Word负责在文档段落中创建运行的位置,因此可以在单词的中间随机分割运行。因此,您可能正在搜索的文本可能是一次运行的一半,另一半运行的一半。使用段落的全文,删除其现有的运行,并添加带有调整后的文本的新运行似乎解决了文本替换的问题。

但是,在段落级别进行替换需要付出代价;你丢失了该段落中的运行格式。例如,如果在段落的中间你加粗了“bits”这个单词,那么在解析文件时你用“bytes”替换了“bits”这个单词,“bytes”这个单词将不再是粗体。因为粗体存储了一个在段落的整个文本体被替换时被删除的运行。附加的代码有一个注释掉的部分,如果需要,它可以在运行级别替换文本。

还应注意,如果要插入的文本包含 n返回字符,则以下内容有效。我们找不到一种方法来插入返回而不为返回之前的每个部分创建一个运行并标记运行addCarriageReturn()。干杯

    package com.healthpartners.hcss.client.external.word.replacement;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

public class TextReplacer {
    private String searchValue;
    private String replacement;

    public TextReplacer(String searchValue, String replacement) {
        this.searchValue = searchValue;
        this.replacement = replacement;
    }

    public void replace(XWPFDocument document) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();

    for (XWPFParagraph xwpfParagraph : paragraphs) {
        replace(xwpfParagraph);
    }
}

private void replace(XWPFParagraph paragraph) {
    if (hasReplaceableItem(paragraph.getText())) {
        String replacedText = StringUtils.replace(paragraph.getText(), searchValue, replacement);

        removeAllRuns(paragraph);

        insertReplacementRuns(paragraph, replacedText);
    }
}

private void insertReplacementRuns(XWPFParagraph paragraph, String replacedText) {
    String[] replacementTextSplitOnCarriageReturn = StringUtils.split(replacedText, "
");

    for (int j = 0; j < replacementTextSplitOnCarriageReturn.length; j++) {
        String part = replacementTextSplitOnCarriageReturn[j];

        XWPFRun newRun = paragraph.insertNewRun(j);
        newRun.setText(part);

        if (j+1 < replacementTextSplitOnCarriageReturn.length) {
            newRun.addCarriageReturn();
        }
    }       
}

private void removeAllRuns(XWPFParagraph paragraph) {
    int size = paragraph.getRuns().size();
    for (int i = 0; i < size; i++) {
        paragraph.removeRun(0);
    }
}

private boolean hasReplaceableItem(String runText) {
    return StringUtils.contains(runText, searchValue);
}

//REVISIT The below can be removed if Michele tests and approved the above less versatile replacement version

//  private void replace(XWPFParagraph paragraph) {
//      for (int i = 0; i < paragraph.getRuns().size()  ; i++) {
//          i = replace(paragraph, i);
//      }
//  }

//  private int replace(XWPFParagraph paragraph, int i) {
//      XWPFRun run = paragraph.getRuns().get(i);
//      
//      String runText = run.getText(0);
//      
//      if (hasReplaceableItem(runText)) {
//          return replace(paragraph, i, run);
//      }
//      
//      return i;
//  }

//  private int replace(XWPFParagraph paragraph, int i, XWPFRun run) {
//      String runText = run.getCTR().getTArray(0).getStringValue();
//      
//      String beforeSuperLong = StringUtils.substring(runText, 0, runText.indexOf(searchValue));
//      
//      String[] replacementTextSplitOnCarriageReturn = StringUtils.split(replacement, "
");
//      
//      String afterSuperLong = StringUtils.substring(runText, runText.indexOf(searchValue) + searchValue.length());
//      
//      Counter counter = new Counter(i);
//      
//      insertNewRun(paragraph, run, counter, beforeSuperLong);
//      
//      for (int j = 0; j < replacementTextSplitOnCarriageReturn.length; j++) {
//          String part = replacementTextSplitOnCarriageReturn[j];
//
//          XWPFRun newRun = insertNewRun(paragraph, run, counter, part);
//          
//          if (j+1 < replacementTextSplitOnCarriageReturn.length) {
//              newRun.addCarriageReturn();
//          }
//      }
//      
//      insertNewRun(paragraph, run, counter, afterSuperLong);
//      
//      paragraph.removeRun(counter.getCount());
//      
//      return counter.getCount();
//  }

//  private class Counter {
//      private int i;
//      
//      public Counter(int i) {
//          this.i = i;
//      }
//      
//      public void increment() {
//          i++;
//      }
//      
//      public int getCount() {
//          return i;
//      }
//  }

//  private XWPFRun insertNewRun(XWPFParagraph xwpfParagraph, XWPFRun run, Counter counter, String newText) {
//      XWPFRun newRun = xwpfParagraph.insertNewRun(counter.i);
//      newRun.getCTR().set(run.getCTR());
//      newRun.getCTR().getTArray(0).setStringValue(newText);
//      
//      counter.increment();
//      
//      return newRun;
//  }
另一答案

我的任务是用单词docx文档中的地图值替换格式$ {key}的文本。上述解决方案是一个很好的起点,但未考虑所有情况:$ {key}不仅可以在多个运行中传播,还可以在运行中的多个文本中传播。因此,我最终得到以下代码:

    private void replace(String inFile, Map<String, String> data, OutputStream out) throws Exception, IOException {
    XWPFDocument doc = new XWPFDocument(OPCPackage.open(inFile));
    for (XWPFParagraph p : doc.getParagraphs()) {
        replace2(p, data);
    }
    for (XWPFTable tbl : doc.getTables()) {
        for (XWPFTableRow row : tbl.getRows()) {
            for (XWPFTableCell cell : row.getTableCells()) {
                for (XWPFParagraph p : cell.getParagraphs()) {
                    replace2(p, data);
                }
            }
        }
    }
    doc.write(out);
}

private void replace2(XWPFParagraph p, Map<String, String> data) {
    String pText = p.getText(); // complete paragraph as string
    if (pText.contains("${")) { // if paragraph does not include our pattern, ignore
        TreeMap<Integer, XWPFRun> posRuns = getPosToRuns(p);
        Pattern pat = Pattern.compile("\$\{(.+?)\}");
        Matcher m = pat.matcher(pText);
        while (m.find()) { // for all patterns in the paragraph
            String g = m.group(1);  // extract key start and end pos
            int s = m.start(1);
            int e = m.end(1);
            String key = g;
            String x = data.get(key);
            if (x == null)
                x = "";
            SortedMap<Integer, XWPFRun> range = posRuns.subMap(s - 2, true, e + 1, true); // get runs which contain the pattern
            boolean found1 = false; // found $
            boolean found2 = false; // found {
            boolean found3 = false; // found }
            XWPFRun prevRun = null; // previous run handled in the loop
            XWPFRun found2Run = null; // run in which { was found
      

以上是关于替换Apache POI XWPF中的文本的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中使用 Apache POI XWPF 在 Word 文档中的横向/纵向之间切换

我想用POI在Word里插入一张图片,这是我的测试代码。执行之后word打不开!错误如图中的提示

POI操作Word模板文本替换(表格文本替换)

java poi xwpf操作word生成一个表格怎么合并单元格,求大神指导!

java word转html 报错 org/apache/poi/xwpf/usermodel/IRunBody

用户WORD模板写文件