Java: Apache POI: Can I get clean text from MS Word (.doc) files?

21,558

Solution 1

There are two options, one provided directly in Apache POI, the other via Apache Tika (which uses Apache POI internally).

The first option is to use WordExtractor, but wrap it in a call to stripFields(String) when calling it. That will remove the text based fields included in the text, things like HYPERLINK that you've seen. Your code would become:

NPOIFSFileSystem fs = new NPOIFSFileSytem(file);
WordExtractor extractor = new WordExtractor(fs.getRoot());

for(String rawText : extractor.getParagraphText()) {
String text = extractor.stripFields(rawText);
System.out.println(text);
}

The other option is to use Apache Tika. Tika provides text extraction, and metadata, for a wide variety of files, so the same code will work for .doc, .docx, .pdf and many others too. To get clean, plain text of your word document (you can also get XHTML if you'd rather), you'd do something like:

TikaConfig tika = TikaConfig.getDefaultConfig();
TikaInputStream stream = TikaInputStream.get(file);
ContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
tika.getParser().parse(input, handler, metadata, new ParseContext());
String text = handler.toString();

Solution 2

This class can read both .doc and .docx files in Java. For this I'm using tika-app-1.2.jar:

/*
 * This class is used to read .doc and .docx files
 * 
 * @author Developer
 *
 */

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL; 
import org.apache.tika.detect.DefaultDetector;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.ContentHandler;

class TextExtractor { 
    private OutputStream outputstream;
    private ParseContext context;
    private Detector detector;
    private Parser parser;
    private Metadata metadata;
    private String extractedText;

    public TextExtractor() {
        context = new ParseContext();
        detector = new DefaultDetector();
        parser = new AutoDetectParser(detector);
        context.set(Parser.class, parser);
        outputstream = new ByteArrayOutputStream();
        metadata = new Metadata();
    }

    public void process(String filename) throws Exception {
        URL url;
        File file = new File(filename);
        if (file.isFile()) {
            url = file.toURI().toURL();
        } else {
            url = new URL(filename);
        }
        InputStream input = TikaInputStream.get(url, metadata);
        ContentHandler handler = new BodyContentHandler(outputstream);
        parser.parse(input, handler, metadata, context); 
        input.close();
    }

    public void getString() {
        //Get the text into a String object
        extractedText = outputstream.toString();
        //Do whatever you want with this String object.
        System.out.println(extractedText);
    }

    public static void main(String args[]) throws Exception {
        if (args.length == 1) {
            TextExtractor textExtractor = new TextExtractor();
            textExtractor.process(args[0]);
            textExtractor.getString();
        } else { 
            throw new Exception();
        }
    }
}

To compile:

javac -cp ".:tika-app-1.2.jar" TextExtractor.java

To run:

java -cp ".:tika-app-1.2.jar" TextExtractor SomeWordDocument.doc

Solution 3

Try this, works for me and is purely a POI solution. You will have to look for the HWPFDocument counterpart though. Make sure the document you are reading predates Word 97, else use XWPFDocument like I do.

InputStream inputstream = new FileInputStream(m_filepath); 
//read the file 
XWPFDocument adoc= new XWPFDocument(inputstream);
//and place it in a xwpf format

aString = new XWPFWordExtractor(adoc).getText();           
//gets the full text

Now if you want certain parts you can use the getparagraphtext but dont use the text extractor, use it directly on the paragraph like this

for (XWPFParagraph p : adoc.getParagraphs()) 
{ 
    System.out.println(p.getParagraphText());
}
Share:
21,558
CosmicGiant
Author by

CosmicGiant

Programmer and Indie Game-developer. Most knowledge in Java and C#, but also have some on Python, C, C++, HTML, CSS, JavaScript and Pascal.

Updated on July 16, 2022

Comments

  • CosmicGiant
    CosmicGiant almost 2 years

    The strings I'm (programmatically) getting from MS Word files when using Apache POI are not the same text I can look at when I open the files with MS Word.

    When using the following code:

    File someFile = new File("some\\path\\MSWFile.doc");
    InputStream inputStrm = new FileInputStream(someFile);
    HWPFDocument wordDoc = new HWPFDocument(inputStrm);
    System.out.println(wordDoc.getText());
    

    the output is a single line with many 'invalid' characters (yes, the 'boxes'), and many unwanted strings, like "FORMTEXT", "HYPERLINK \l "_Toc##########"" ('#' being numeric digits), "PAGEREF _Toc########## \h 4", etc.

    The following code "fixes" the single-line problem, but maintains all the invalid characters and unwanted text:

    File someFile = new File("some\\path\\MSWFile.doc");
    InputStream inputStrm = new FileInputStream(someFile);
    WordExtractor wordExtractor = new WordExtractor(inputStrm);
    for(String paragraph:wordExtractor.getParagraphText()){
      System.out.println(paragraph);
    }
    

    I don't know if I'm using the wrong method for extracting the text, but that's what I've come up with when looking at POI's quick-guide. If I am, what is the correct approach?

    If that output is correct, is there a standard way for getting rid of the unwanted text, or will I have to write a filter of my own?