Wyodrębnianie tekstu z dokumentów programu Word jest często wykonywane w różnych scenariuszach. Na przykład, aby przeanalizować tekst, wyodrębnić określone sekcje dokumentu i połączyć je w jeden dokument i tak dalej. W tym artykule dowiesz się, jak programowo wyodrębnić tekst z dokumentów Worda w Javie. Ponadto omówimy, jak dynamicznie wyodrębniać zawartość między określonymi elementami, takimi jak akapity, tabele itp.
- Biblioteka Java do wyodrębniania tekstu z programu Word DOC DOCX
- Ekstrakcja tekstu Java w dokumentach Word
- Wyodrębnij tekst z dokumentu programu Word w Javie
Biblioteka Java do wyodrębniania tekstu z dokumentów programu Word
Aspose.Words for Java to potężna biblioteka, która pozwala tworzyć od podstaw dokumenty MS Word. Ponadto pozwala manipulować istniejącymi dokumentami Word w celu szyfrowania, konwersji, ekstrakcji tekstu itp. Użyjemy tej biblioteki do wyodrębnienia tekstu z dokumentów Word DOCX lub DOC. Możesz pobrać JAR API lub zainstalować go przy użyciu następujących konfiguracji Mavena.
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://repository.aspose.com/repo/</url>
</repository>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>22.6</version>
<type>pom</type>
</dependency>
Ekstrakcja tekstu w Word DOC/DOCX w Javie
Dokument MS Word składa się z różnych elementów, takich jak akapity, tabele, obrazy itp. Dlatego wymagania dotyczące ekstrakcji tekstu mogą się różnić w zależności od scenariusza. Na przykład może być konieczne wyodrębnienie tekstu między akapitami, zakładkami, komentarzami itp.
Każdy typ elementu w Word DOC/DOCX jest reprezentowany jako węzeł. Dlatego, aby przetworzyć dokument, będziesz musiał bawić się węzłami. Zacznijmy więc i zobaczmy, jak wyodrębnić tekst z dokumentów programu Word w różnych scenariuszach.
Wyodrębnij tekst z Word DOC w Javie
W tej sekcji zamierzamy zaimplementować ekstraktor tekstu Java dla dokumentów programu Word, a przepływ pracy podczas wyodrębniania tekstu wyglądałby następująco:
- Najpierw zdefiniujemy węzły, które chcemy uwzględnić w procesie ekstrakcji tekstu.
- Następnie wyodrębnimy zawartość między określonymi węzłami (w tym lub z wyłączeniem węzłów początkowych i końcowych).
- Na koniec wykorzystamy klon wyodrębnionych węzłów np. do stworzenia nowego dokumentu Worda składającego się z wyodrębnionej treści.
Napiszmy teraz metodę o nazwie extractContent, do której przekażemy węzły i kilka innych parametrów w celu przeprowadzenia ekstrakcji tekstu. Ta metoda przeanalizuje dokument i sklonuje węzły. Poniżej przedstawiono parametry, które przekażemy tej metodzie.
- startNode i endNode odpowiednio jako punkty początkowe i końcowe wyodrębniania zawartości. Mogą to być zarówno węzły na poziomie bloku (Akapit , Tabela), jak i na poziomie wiersza (np. Run, FieldStart, BookmarkStart itp.).
- Aby przekazać pole, należy przekazać odpowiedni obiekt FieldStart.
- Aby przekazać zakładki, należy przekazać węzły BookmarkStart i BookmarkEnd.
- W przypadku komentarzy należy używać węzłów CommentRangeStart i CommentRangeEnd.
- isInclusive określa, czy znaczniki są uwzględniane w ekstrakcji, czy nie. Jeśli ta opcja jest ustawiona na false i zostanie przekazany ten sam węzeł lub kolejne węzły, zwrócona zostanie pusta lista.
Poniżej przedstawiono kompletną implementację metody extractContent, która wyodrębnia zawartość między przekazywanymi węzłami.
// Pełne przykłady i pliki danych można znaleźć na stronie https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
// Najpierw sprawdź, czy węzły przekazane do tej metody są prawidłowe do użycia.
verifyParameterNodes(startNode, endNode);
// Utwórz listę do przechowywania wyodrębnionych węzłów.
ArrayList nodes = new ArrayList();
// Zachowaj zapis oryginalnych węzłów przekazanych do tej metody, abyśmy mogli w razie potrzeby podzielić węzły znaczników.
Node originalStartNode = startNode;
Node originalEndNode = endNode;
// Wyodrębnij zawartość na podstawie węzłów na poziomie bloku (akapity i tabele). Przechodź przez węzły nadrzędne, aby je znaleźć.
// Podzielimy zawartość pierwszego i ostatniego węzła w zależności od tego, czy węzły znaczników są wbudowane
while (startNode.getParentNode().getNodeType() != NodeType.BODY)
startNode = startNode.getParentNode();
while (endNode.getParentNode().getNodeType() != NodeType.BODY)
endNode = endNode.getParentNode();
boolean isExtracting = true;
boolean isStartingNode = true;
boolean isEndingNode;
// Bieżący węzeł, który wyodrębniamy z dokumentu.
Node currNode = startNode;
// Rozpocznij wyodrębnianie zawartości. Przetwarzaj wszystkie węzły na poziomie bloku iw razie potrzeby rozdzielaj pierwszy i ostatni węzły, aby zachować formatowanie akapitów.
// Metoda jest nieco bardziej złożona niż zwykły ekstraktor, ponieważ musimy wziąć pod uwagę wyodrębnianie za pomocą wbudowanych węzłów, pól, zakładek itp., Aby była naprawdę użyteczna.
while (isExtracting) {
// Sklonuj bieżący węzeł i jego elementy podrzędne, aby uzyskać kopię.
/*System.out.println(currNode.getNodeType());
if(currNode.getNodeType() == NodeType.EDITABLE_RANGE_START
|| currNode.getNodeType() == NodeType.EDITABLE_RANGE_END)
{
currNode = currNode.nextPreOrder(currNode.getDocument());
}*/
System.out.println(currNode);
System.out.println(endNode);
CompositeNode cloneNode = null;
///cloneNode = (CompositeNode) currNode.deepClone(true);
Node inlineNode = null;
if(currNode.isComposite())
{
cloneNode = (CompositeNode) currNode.deepClone(true);
}
else
{
if(currNode.getNodeType() == NodeType.BOOKMARK_END)
{
Paragraph paragraph = new Paragraph(currNode.getDocument());
paragraph.getChildNodes().add(currNode.deepClone(true));
cloneNode = (CompositeNode)paragraph.deepClone(true);
}
}
isEndingNode = currNode.equals(endNode);
if (isStartingNode || isEndingNode) {
// Musimy przetworzyć każdy znacznik osobno, więc zamiast tego przekaż go osobnej metodzie.
if (isStartingNode) {
processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
isStartingNode = false;
}
// Warunek musi być oddzielny, ponieważ znaczniki początku i końca na poziomie bloku mogą być tym samym węzłem.
if (isEndingNode) {
processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
isExtracting = false;
}
} else
// Węzeł nie jest znacznikiem początkowym ani końcowym, po prostu dodaj kopię do listy.
nodes.add(cloneNode);
// Przejdź do następnego węzła i wyodrębnij go. Jeśli następny węzeł ma wartość null, oznacza to, że reszta treści znajduje się w innej sekcji.
if (currNode.getNextSibling() == null && isExtracting) {
// Przejdź do następnej sekcji.
Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
currNode = nextSection.getBody().getFirstChild();
} else {
// Przejdź do następnego węzła w ciele.
currNode = currNode.getNextSibling();
}
}
// Zwróć węzły między znacznikami węzłów.
return nodes;
}
Niektóre metody pomocnicze są również wymagane przez metodę extractContent do wykonania operacji wyodrębniania tekstu, które podano poniżej.
/**
* Sprawdza, czy parametry wejściowe są poprawne i czy można ich użyć. Zgłasza wyjątek
* jeśli jest jakiś problem.
*/
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
// Kolejność przeprowadzania tych kontroli jest ważna.
if (startNode == null)
throw new IllegalArgumentException("Start node cannot be null");
if (endNode == null)
throw new IllegalArgumentException("End node cannot be null");
if (!startNode.getDocument().equals(endNode.getDocument()))
throw new IllegalArgumentException("Start node and end node must belong to the same document");
if (startNode.getAncestor(NodeType.BODY) == null || endNode.getAncestor(NodeType.BODY) == null)
throw new IllegalArgumentException("Start node and end node must be a child or descendant of a body");
// Sprawdź, czy węzeł końcowy znajduje się za węzłem początkowym w drzewie DOM
// Najpierw sprawdź, czy są w różnych sekcjach, a jeśli nie, sprawdź
// ich położenie w ciele tej samej sekcji, w której się znajdują.
Section startSection = (Section) startNode.getAncestor(NodeType.SECTION);
Section endSection = (Section) endNode.getAncestor(NodeType.SECTION);
int startIndex = startSection.getParentNode().indexOf(startSection);
int endIndex = endSection.getParentNode().indexOf(endSection);
if (startIndex == endIndex) {
if (startSection.getBody().indexOf(startNode) > endSection.getBody().indexOf(endNode))
throw new IllegalArgumentException("The end node must be after the start node in the body");
} else if (startIndex > endIndex)
throw new IllegalArgumentException("The section of end node must be after the section start node");
}
/**
* Sprawdza, czy przekazany węzeł jest węzłem wbudowanym.
*/
private static boolean isInline(Node node) throws Exception {
// Sprawdź, czy węzeł jest potomkiem węzła akapitu lub tabeli, a także nie jest a
// akapit lub tabela akapit wewnątrz klasy komentarza, który jest potomkiem
// paragraf jest możliwy.
return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}
/**
* Usuwa zawartość przed lub po znaczniku w sklonowanym węźle, w zależności od tego
* od rodzaju znacznika.
*/
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
boolean isStartMarker, boolean isEndMarker) throws Exception {
// Jeśli mamy do czynienia z węzłem na poziomie bloku, po prostu sprawdź, czy powinien zostać uwzględniony
// i dodaj go do listy.
if (!isInline(node)) {
// Nie dodawaj węzła dwa razy, jeśli znaczniki są tym samym węzłem
if (!(isStartMarker && isEndMarker)) {
if (isInclusive)
nodes.add(cloneNode);
}
return;
}
// Jeśli znacznik jest węzłem FieldStart, sprawdź, czy ma być uwzględniony, czy nie.
// Dla uproszczenia zakładamy, że FieldStart i FieldEnd pojawiają się w tym samym
// ustęp.
if (node.getNodeType() == NodeType.FIELD_START) {
// Jeśli znacznik jest węzłem początkowym i nie jest uwzględniony, przejdź do końca
// pole.
// Jeśli znacznik jest węzłem końcowym i ma być uwzględniony, przejdź do końca
// pole, aby pole nie zostało usunięte.
if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
node = node.getNextSibling();
}
}
// Jeśli któryś ze znaczników jest częścią komentarza, wówczas dołączymy sam komentarz
// musisz przesunąć wskaźnik do przodu do Komentarza
// węzeł znaleziony za węzłem CommentRangeEnd.
if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
node = node.getNextSibling();
}
// Znajdź odpowiedni węzeł w naszym sklonowanym węźle według indeksu i zwróć go.
// Jeśli węzeł początkowy i końcowy są takie same, niektóre węzły podrzędne mogą już mieć
// został usunięty. Odejmij
// różnicę, aby uzyskać właściwy indeks.
int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();
// Liczba węzłów podrzędnych identyczna.
if (indexDiff == 0)
node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
else
node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);
// Usuń węzły do/od znacznika.
boolean isSkip;
boolean isProcessing = true;
boolean isRemoving = isStartMarker;
Node nextNode = cloneNode.getFirstChild();
while (isProcessing && nextNode != null) {
Node currentNode = nextNode;
isSkip = false;
if (currentNode.equals(node)) {
if (isStartMarker) {
isProcessing = false;
if (isInclusive)
isRemoving = false;
} else {
isRemoving = true;
if (isInclusive)
isSkip = true;
}
}
nextNode = nextNode.getNextSibling();
if (isRemoving && !isSkip)
currentNode.remove();
}
// Po przetworzeniu węzeł złożony może stać się pusty. Jeśli tak, nie dołączaj
// to.
if (!(isStartMarker && isEndMarker)) {
if (cloneNode.hasChildNodes())
nodes.add(cloneNode);
}
}
public static Document generateDocument(Document srcDoc, ArrayList nodes) throws Exception {
// Utwórz pusty dokument.
Document dstDoc = new Document();
// Usuń pierwszy akapit z pustego dokumentu.
dstDoc.getFirstSection().getBody().removeAllChildren();
// Zaimportuj każdy węzeł z listy do nowego dokumentu. Zachowaj oryginał
// formatowanie węzła.
NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);
for (Node node : (Iterable<Node>) nodes) {
Node importNode = importer.importNode(node, true);
dstDoc.getFirstSection().getBody().appendChild(importNode);
}
// Zwróć wygenerowany dokument.
return dstDoc;
}
Teraz jesteśmy gotowi do wykorzystania tych metod i wyodrębnienia tekstu z dokumentu Word.
Java Wyodrębnij tekst między akapitami w dokumencie Word DOC
Zobaczmy, jak wyodrębnić zawartość między dwoma akapitami w dokumencie Word DOCX. Poniżej przedstawiono kroki, aby wykonać tę operację w Javie.
- Najpierw załaduj dokument programu Word przy użyciu klasy Document.
- Uzyskaj odwołanie do akapitu początkowego i końcowego do dwóch obiektów za pomocą metody Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool).
- Wywołaj metodę extractContent(startPara, endPara, true), aby wyodrębnić węzły do obiektu.
- Wywołaj metodę pomocnika generateDocument(Document, extractedNodes), aby utworzyć dokument składający się z wyodrębnionej treści.
- Na koniec zapisz zwrócony dokument przy użyciu metody Document.save(String).
Poniższy przykładowy kod pokazuje, jak wyodrębnić tekst między 7. a 11. akapitem w Word DOCX w Javie.
// Załaduj dokument
Document doc = new Document("TestFile.doc");
// Zbierz węzły. Metoda GetChild używa indeksu opartego na 0
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// Wyodrębnij zawartość między tymi węzłami w dokumencie. Uwzględnij te
// znaczniki w ekstrakcji.
ArrayList extractedNodes = extractContent(startPara, endPara, true);
// Wstaw zawartość do nowego oddzielnego dokumentu i zapisz ją na dysku.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");
Java Wyodrębnij tekst z DOC - między różnymi typami węzłów
Możesz także wyodrębniać zawartość między różnymi typami węzłów. Dla demonstracji wyodrębnijmy zawartość między akapitem a tabelą i zapiszmy ją w nowym dokumencie programu Word. Poniżej przedstawiono kroki, aby wyodrębnić tekst między różnymi węzłami w dokumencie Word w Javie.
- Załaduj dokument programu Word przy użyciu klasy Document.
- Pobierz odwołanie do węzła początkowego i końcowego do dwóch obiektów za pomocą metody Document.getFirstSection().getChild(NodeType, int, bool).
- Wywołaj metodę extractContent(startPara, endPara, true), aby wyodrębnić węzły do obiektu.
- Wywołaj metodę pomocnika generateDocument(Document, extractedNodes), aby utworzyć dokument składający się z wyodrębnionej treści.
- Zapisz zwrócony dokument przy użyciu metody Document.save(String).
Poniższy przykładowy kod pokazuje, jak wyodrębnić tekst między akapitem a tabelą w DOCX przy użyciu języka Java.
// Załaduj dokumenty
Document doc = new Document("TestFile.doc");
// Uzyskaj odniesienie do akapitu początkowego
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);
// Wyodrębnij zawartość między tymi węzłami w dokumencie. Uwzględnij te znaczniki w ekstrakcji.
ArrayList extractedNodes = extractContent(startPara, endTable, true);
// Odwróćmy tablicę, aby ułatwić wstawianie zawartości z powrotem do dokumentu.
Collections.reverse(extractedNodes);
while (extractedNodes.size() > 0) {
// Wstaw ostatni węzeł z odwróconej listy
endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
// Usuń ten węzeł z listy po wstawieniu.
extractedNodes.remove(0);
}
// Zapisz wygenerowany dokument na dysku.
doc.save("output.doc");
Java Wyodrębnianie tekstu z DOCX - Między akapitami na podstawie stylów
Sprawdźmy teraz, jak wyodrębnić treść między akapitami na podstawie stylów. W celu demonstracji wyodrębnimy zawartość między pierwszym „Nagłówkiem 1” a pierwszym „Nagłówkiem 3” w dokumencie programu Word. Poniższe kroki pokazują, jak to osiągnąć w Javie.
- Najpierw załaduj dokument programu Word przy użyciu klasy Document.
- Następnie wyodrębnij akapity do obiektu za pomocą metody pomocniczej ParagrafyByStyleName(Document, “Heading 1”).
- Wyodrębnij akapity do innego obiektu za pomocą metody pomocniczej ParagrafyByStyleName(Document, “Heading 3”).
- Wywołaj metodę extractContent(startPara, endPara, true) i przekaż pierwsze elementy w obu tablicach akapitów jako pierwszy i drugi parametr.
- Wywołaj metodę pomocnika generateDocument(Document, extractedNodes), aby utworzyć dokument składający się z wyodrębnionej treści.
- Na koniec zapisz zwrócony dokument przy użyciu metody Document.save(String).
Poniższy przykład kodu pokazuje, jak wyodrębnić zawartość między akapitami na podstawie stylów.
// Załaduj dokument
Document doc = new Document(dataDir + "TestFile.doc");
// Zbierz listę akapitów, używając odpowiednich stylów nagłówków.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");
// Użyj pierwszego wystąpienia akapitów z tymi stylami.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);
// Wyodrębnij zawartość między tymi węzłami w dokumencie. Nie uwzględniaj tych znaczników w ekstrakcji.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);
// Wstaw zawartość do nowego oddzielnego dokumentu i zapisz ją na dysku.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");
Ekstraktor tekstu Java Word — Czytaj więcej
Możesz zapoznać się z innymi scenariuszami wyodrębniania tekstu z dokumentów programu Word, korzystając z tego artykułu w dokumentacji.
Java API do wyodrębniania tekstu z DOC/DOCX — Uzyskaj bezpłatną licencję
Możesz uzyskać tymczasową licencję na używanie Aspose.Words for Java bez ograniczeń ewaluacyjnych.
Wniosek
W tym artykule nauczyłeś się, jak wyodrębnić tekst z MS Word DOC DOCX w Javie. Ponadto widziałeś, jak programowo wyodrębniać zawartość między podobnymi lub różnymi typami węzłów w dokumencie programu Word. W ten sposób możesz zbudować własny ekstraktor tekstu MS Word w Javie. Poza tym możesz poznać inne funkcje Aspose.Words for Java, korzystając z dokumentacji. Jeśli masz jakieś pytania, daj nam znać za pośrednictwem naszego forum.