Java: Apache POI: могу ли я получить чистый текст из файлов MS Word (.doc)?

Строки, которые я (программно) получаю из файлов MS Word при использовании Apache POI, не являются тем же самым текстом, на который я могу посмотреть, когда я открываю файлы с помощью MS Word.

При использовании следующего кода:

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

вывод представляет собой одну строку со многими "недопустимыми" символами (да, "полями" ) и множеством ненужных строк, например "FORMTEXT", "HYPERLINK \l "_Toc##########"" ( "#" - числовые цифры) "PAGEREF _Toc########## \h 4" и т.д.

Следующий код "исправляет" проблему с одной строкой, но сохраняет все недопустимые символы и нежелательный текст:

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);
}

Я не знаю, использую ли я неправильный метод для извлечения текста, но это то, что я придумал при просмотре POI quick -руководство. Если да, то каков правильный подход?

Если этот вывод правильный, есть ли стандартный способ избавиться от нежелательного текста или мне придется написать собственный фильтр?

Ответ 1

Существует два варианта, один из которых предоставляется непосредственно в Apache POI, а другой - через Apache Tika (который использует внутренний IP-адрес Apache).

Первый вариант - использовать WordExtractor, но завершите его при вызове stripFields(String) при его вызове. Это позволит удалить текстовые поля, включенные в текст, такие вещи, как HYPERLINK, которые вы видели. Ваш код будет выглядеть следующим образом:

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);
}

Другой вариант - использовать Apache Tika. Tika предоставляет извлечение текста и метаданные для самых разных файлов, поэтому один и тот же код будет работать для .doc,.docx,.pdf и многих других. Чтобы получить чистый текстовый документ (вы также можете получить XHTML, если хотите), вы бы сделали что-то вроде:

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();

Ответ 2

Этот класс может читать файлы .doc и .docx в Java. Для этого я использую 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();
        }
    }
}

Скомпилировать:

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

Для запуска:

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

Ответ 3

Попробуйте это, работает для меня и является просто решением POI. Однако вам придется искать партнера HWPFDocument. Убедитесь, что документ, который вы читаете, предшествует Word 97, иначе используйте XWPFDocument, как я.

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

Теперь, если вам нужны определенные части, вы можете использовать getparagraphtext, но не используйте текстовый экстрактор, используйте его непосредственно в абзаце, подобном этому

for (XWPFParagraph p : adoc.getParagraphs()) 
{ 
    System.out.println(p.getParagraphText());
}