6

I'm currently trying to work on the code mentioned on a previous post called Replacing a text in Apache POI XWPF.

I have tried the below and it works but I don't know if I am missing anything. When I run the code the text is not replaced but added onto the end of what was searched. For example I have created a basic word document and entered the text "test". In the below code when I run it I eventually get the new document with the text "testDOG".

I have had to change the original code from String text = r.getText(0) to String text = r.toString() because I kept getting a NullError while running the code.

import java.io.*;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;


public class testPOI {

    public static void main(String[] args) throws Exception{

    String filepath = "F:\\MASTER_DOC.docx";
    String outpath = "F:\\Test.docx";

    XWPFDocument doc = new XWPFDocument(OPCPackage.open(filepath));
    for (XWPFParagraph p : doc.getParagraphs()){
        for (XWPFRun r : p.getRuns()){
            String text = r.toString();
            if(text.contains("test")) {
                text = text.replace("test", "DOG");
                r.setText(text);
            }
        }
    }
   doc.write(new FileOutputStream(outpath));
}

EDIT: Thanks for your help everyone. I browsed around and found a solution on Replace table column value in Apache POI

Community
  • 1
  • 1
Michael
  • 87
  • 1
  • 2
  • 11
  • What version of Apache POI is with this with? And if it isn't the latest, have you tried upgrading? – Gagravarr Jun 13 '14 at 12:49
  • Thanks for the reply, I'm using the latest stable version 3.10-FINAL. I'll give James idea a go. Thanks. – Michael Jun 15 '14 at 10:01

4 Answers4

13

This method replace search Strings in paragraphs and is able to work with Strings spanning over more than one Run.

  private long replaceInParagraphs(Map<String, String> replacements, List<XWPFParagraph> xwpfParagraphs) {
    long count = 0;
    for (XWPFParagraph paragraph : xwpfParagraphs) {
      List<XWPFRun> runs = paragraph.getRuns();

      for (Map.Entry<String, String> replPair : replacements.entrySet()) {    
        String find = replPair.getKey();
        String repl = replPair.getValue();
        TextSegement found = paragraph.searchText(find, new PositionInParagraph());
        if ( found != null ) {
          count++;
          if ( found.getBeginRun() == found.getEndRun() ) {
            // whole search string is in one Run
            XWPFRun run = runs.get(found.getBeginRun());
            String runText = run.getText(run.getTextPosition());
            String replaced = runText.replace(find, repl);
            run.setText(replaced, 0);
          } else {
            // The search string spans over more than one Run
            // Put the Strings together
            StringBuilder b = new StringBuilder();
            for (int runPos = found.getBeginRun(); runPos <= found.getEndRun(); runPos++) {
              XWPFRun run = runs.get(runPos);
              b.append(run.getText(run.getTextPosition()));
            }                       
            String connectedRuns = b.toString();
            String replaced = connectedRuns.replace(find, repl);

            // The first Run receives the replaced String of all connected Runs
            XWPFRun partOne = runs.get(found.getBeginRun());
            partOne.setText(replaced, 0);
            // Removing the text in the other Runs.
            for (int runPos = found.getBeginRun()+1; runPos <= found.getEndRun(); runPos++) {
              XWPFRun partNext = runs.get(runPos);
              partNext.setText("", 0);
            }                          
          }
        }
      }      
    }
    return count;
  }
Josh
  • 869
  • 10
  • 10
  • 5
    Very nice! Combined this implementation with logic from https://stackoverflow.com/a/30131160/4411975 and now we have a way to do a global search and replace :) – Ray W Sep 18 '18 at 18:20
  • 2
    Worth noticing that, ``run.getPosition()`` returns -1 most of the cases. But it does not effect when there is only one text postion per a run. But, technically it can have any number of textPositions and I've experienced such cases. So, the best way is to ``getCTR ()`` for run and terate through each the run for count of textPositions. Number of textPositions are equal to ``ctrRun.sizeOfTArray() `` – Gayan Kavirathne Jan 29 '19 at 14:06
  • 1
    it's the best answer ever, I've never found this implemation in another place in internet. Thanks – Murilo Góes de Almeida Sep 17 '20 at 17:42
4

Your logic is not quite right. You need to collate all the text in the runs first and then do the replace. You also need to remove all runs for the paragraph and add a new single run if a match on "test" is found.

Try this instead:

public class testPOI {

    public static void main(String[] args) throws Exception{

        String filepath = "F:\\MASTER_DOC.docx";
        String outpath = "F:\\Test.docx";

        XWPFDocument doc = new XWPFDocument(new FileInputStream(filepath));
        for (XWPFParagraph p : doc.getParagraphs()){

            int numberOfRuns = p.getRuns().size();

            // Collate text of all runs
            StringBuilder sb = new StringBuilder();
            for (XWPFRun r : p.getRuns()){
                int pos = r.getTextPosition();
                if(r.getText(pos) != null) {
                    sb.append(r.getText(pos));
                }
            }

            // Continue if there is text and contains "test"
            if(sb.length() > 0 && sb.toString().contains("test")) {
                // Remove all existing runs
                for(int i = 0; i < numberOfRuns; i++) {
                    p.removeRun(i);
                }
                String text = sb.toString().replace("test", "DOG");
                // Add new run with updated text
                XWPFRun run = p.createRun();
                run.setText(text);
                p.addRun(run);
            }
        }
       doc.write(new FileOutputStream(outpath));
    }
}
JamesB
  • 7,774
  • 2
  • 22
  • 21
  • It works a bit but not completely. If for example you use the below text it doesn't work and the format goes all wrong. Jeget lacinia sem commodo ac. Fusce molestie sodales suscipit. Maecenas tortor sem, rutrum vel **test** eu, elementum fermentum sem. – Michael Jun 16 '14 at 12:08
  • Thanks for your help James. I found a solution on [link] (http://stackoverflow.com/questions/8398259/replace-table-column-value-in-apache-poi?rq=1) – Michael Jun 16 '14 at 12:35
1

Worth noticing that, run.getPosition() returns -1 most of the cases. But it does not effect when there is only one text postion per a run. But, technically it can have any number of textPositions and I've experienced such cases. So, the best way is to getCTR () for run and terate through each the run for count of textPositions. Number of textPositions are equal to ctrRun.sizeOfTArray()

A sample code

for (XWPFRun run : p.getRuns()){
     CTR ctrRun =  run.getCTR();
     int sizeOfCtr = ctrRun.sizeOfTArray();
     for(int textPosition=0; textPosition<sizeOfCtr){
            String text = run.getText(textPosition);
            if(text.contains("test")) {
                 text = text.replace("test", "DOG");
                 r.setText(text,textPosition);
            }
     }

 }
Gayan Kavirathne
  • 2,909
  • 2
  • 18
  • 26
0

just change text for every run in your paragraph, and then save the file. this code worked for mi

XWPFDocument doc = new XWPFDocument(new FileInputStream(filepath));
for (XWPFParagraph p : doc.getParagraphs()) {
    StringBuilder sb = new StringBuilder();
    for (XWPFRun r : p.getRuns()) {
    String text = r.getText(0);
    if (text != null && text.contains("variable1")) {
        text = text.replace("variable1", "valeur1");
        r.setText(text, 0);
    }
    if (text != null && text.contains("variable2")) {
        text = text.replace("variable2", "valeur2");
        r.setText(text, 0);
    }
    if (text != null && text.contains("variable3")) {
        text = text.replace("variable3", "valeur3");
        r.setText(text, 0);
    }
    }

}

doc.write(new FileOutputStream(outpath));
Gor
  • 61
  • 8