Extrahujte text z dokumentů MS Word v Javě

V různých případech může být nutné programově extrahovat text z dokumentů aplikace Word. Například pro analýzu textu, extrakci určitých sekcí atd. Pro takové případy tento článek poskytuje rychlou a snadno implementovatelnou metodu pro extrakci textu z dokumentů Wordu v Javě. Také vám umožní extrahovat text mezi různými sekcemi/prvky dokumentu aplikace Word.

Java knihovna pro extrahování textu z dokumentů aplikace Word

Aspose.Words for Java je výkonná knihovna, která vám umožňuje vytvářet dokumenty MS Word od začátku. Navíc vám umožňuje manipulovat se stávajícími dokumenty Wordu pro šifrování, konverzi, extrakci textu atd. Tuto knihovnu použijeme k extrahování textu z dokumentů Word DOCX nebo DOC. Můžete si stáhnout JAR API nebo jej nainstalovat pomocí následujících konfigurací Maven.

<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>

Pochopení struktury dokumentu aplikace Word

Dokument MS Word se skládá z různých prvků, které zahrnují odstavce, tabulky, obrázky atd. Požadavky na extrakci textu se proto mohou v jednotlivých scénářích lišit. Můžete například potřebovat extrahovat text mezi odstavci, záložkami, komentáři atd.

Každý typ prvku ve Wordu DOC/DOCX je reprezentován jako uzel. Pro zpracování dokumentu si tedy budete muset pohrát s uzly. Takže začněme a uvidíme, jak extrahovat text z dokumentů aplikace Word v různých scénářích.

Extrahujte text z Word DOC v Javě

V této části se chystáme implementovat extraktor textu Java pro dokumenty Word a pracovní postup extrakce textu by byl následující:

  • Nejprve definujeme uzly, které chceme zahrnout do procesu extrakce textu.
  • Potom extrahujeme obsah mezi zadanými uzly (včetně nebo vyloučení počátečního a koncového uzlu).
  • Nakonec použijeme klon extrahovaných uzlů, např. k vytvoření nového dokumentu Word sestávajícího z extrahovaného obsahu.

Pojďme si nyní napsat metodu s názvem extractContent, které předáme uzly a některé další parametry pro provedení extrakce textu. Tato metoda analyzuje dokument a klonuje uzly. Následují parametry, které této metodě předáme.

  1. startNode a endNode jako počáteční a koncové body pro extrakci obsahu. Mohou to být uzly na úrovni bloku (Paragraph , Table) nebo inline (např. Run, FieldStart, BookmarkStart atd.).
    1. Chcete-li předat pole, měli byste předat odpovídající objekt FieldStart.
    2. Pro předání záložek by měly být předány uzly BookmarkStart a BookmarkEnd.
    3. Pro komentáře by měly být použity uzly CommentRangeStart a CommentRangeEnd.
  2. isInclusive definuje, zda jsou značky zahrnuty do extrakce nebo ne. Pokud je tato možnost nastavena na hodnotu false a je předán stejný uzel nebo po sobě jdoucí uzly, vrátí se prázdný seznam.

Následuje kompletní implementace metody extractContent, která extrahuje obsah mezi předávanými uzly.

// Úplné příklady a datové soubory najdete na https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
    // Nejprve zkontrolujte, zda jsou uzly předané této metodě platné pro použití.
    verifyParameterNodes(startNode, endNode);

    // Vytvořte seznam pro uložení extrahovaných uzlů.
    ArrayList nodes = new ArrayList();

    // Zaznamenejte si původní uzly předané této metodě, abychom mohli v případě potřeby rozdělit značkovací uzly.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Extrahujte obsah na základě uzlů na úrovni bloku (odstavců a tabulek). Procházejte nadřazené uzly a najděte je.
    // Obsah prvního a posledního uzlu rozdělíme podle toho, zda jsou uzly značky vložené
    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;
    // Aktuální uzel, který extrahujeme z dokumentu.
    Node currNode = startNode;

    // Začněte extrahovat obsah. Zpracujte všechny uzly na úrovni bloku a v případě potřeby konkrétně rozdělte první a poslední uzel, aby bylo zachováno formátování odstavce.
    // Metoda je o něco složitější než běžný extraktor, protože musíme zohlednit extrakci pomocí vložených uzlů, polí, záložek atd., aby byla opravdu užitečná.
    while (isExtracting) {
        // Klonujte aktuální uzel a jeho potomky, abyste získali kopii.
        /*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) {
            // Potřebujeme zpracovat každou značku zvlášť, takže ji místo toho předáme samostatné metodě.
            if (isStartingNode) {
                processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // Podmíněné musí být oddělené, protože značky začátku a konce úrovně bloku mohou být stejný uzel.
            if (isEndingNode) {
                processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        } else
            // Uzel není značka začátku nebo konce, jednoduše přidejte kopii do seznamu.
            nodes.add(cloneNode);

        // Přesuňte se na další uzel a extrahujte jej. Pokud je další uzel null, znamená to, že zbytek obsahu se nachází v jiné sekci.
        if (currNode.getNextSibling() == null && isExtracting) {
            // Přejít na další sekci.
            Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
            currNode = nextSection.getBody().getFirstChild();
        } else {
            // Přesuňte se na další uzel v těle.
            currNode = currNode.getNextSibling();
        }
    }

    // Vraťte uzly mezi značky uzlů.
    return nodes;
}

Některé pomocné metody jsou také vyžadovány metodou extractContent k provedení operace extrakce textu, které jsou uvedeny níže.

/**
 * Zkontroluje, zda jsou vstupní parametry správné a lze je použít. Vyvolá výjimku
 * pokud je nějaký problém.
 */
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
	// Důležité je pořadí, ve kterém se tyto kontroly provádějí.
	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");

	// Zkontrolujte, že koncový uzel je za počátečním uzlem ve stromu DOM
	// Nejprve zkontrolujte, zda jsou v různých sekcích, a poté, zda nejsou
	// jejich pozice v těle stejné sekce, ve které se nacházejí.
	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");
}

/**
 * Zkontroluje, zda je předaný uzel vloženým uzlem.
 */
private static boolean isInline(Node node) throws Exception {
	// Otestujte, zda je uzel desendantem uzlu odstavce nebo tabulky a také není a
	// odstavec nebo tabulka odstavec uvnitř třídy komentářů, která je pocházející z
	// pararaf je možný.
	return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
			&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}

/**
 * Odebere obsah před nebo za značkou v závislosti na klonovaném uzlu
 * na typu značky.
 */
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
		boolean isStartMarker, boolean isEndMarker) throws Exception {
	// Pokud máme co do činění s uzlem na úrovni bloku, podívejte se, zda by měl být zahrnut
	// a přidejte jej do seznamu.
	if (!isInline(node)) {
		// Nepřidávejte uzel dvakrát, pokud jsou značky stejný uzel
		if (!(isStartMarker && isEndMarker)) {
			if (isInclusive)
				nodes.add(cloneNode);
		}
		return;
	}

	// Pokud je značka uzel FieldStart, zkontrolujte, zda má být zahrnuta nebo ne.
	// Pro jednoduchost předpokládáme, že FieldStart a FieldEnd se objevují stejně
	// odstavec.
	if (node.getNodeType() == NodeType.FIELD_START) {
		// Pokud je značka počátečním uzlem a není zahrnuta, přejděte na konec
		// pole.
		// Pokud je značka koncovým uzlem a má být zahrnuta, přesuňte se na konec
		// pole, takže pole nebude odstraněno.
		if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
			while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
				node = node.getNextSibling();

		}
	}

	// Pokud je některá značka součástí komentáře, pak zahrneme komentář samotný my
	// je třeba přesunout ukazatel dopředu na komentář
	// uzel nalezen za uzlem CommentRangeEnd.
	if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
		while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
			node = node.getNextSibling();

	}

	// Najděte odpovídající uzel v našem klonovaném uzlu podle indexu a vraťte jej.
	// Pokud jsou počáteční a koncový uzel stejné, některé podřízené uzly již mohou mít
	// byla odstraněna. Odečtěte
	// rozdíl získat správný index.
	int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();

	// Počet podřízených uzlů je stejný.
	if (indexDiff == 0)
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
	else
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);

	// Odstraňte uzly až k/od značky.
	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 zpracování se může složený uzel vyprázdnto. Pokud má, nezahrnujte
	// to.
	if (!(isStartMarker && isEndMarker)) {
		if (cloneNode.hasChildNodes())
			nodes.add(cloneNode);
	}
}

public static Document generateDocument(Document srcDoc, ArrayList nodes) throws Exception {

	// Vytvořte prázdný dokument.
	Document dstDoc = new Document();
	// Odeberte první odstavec z prázdného dokumentu.
	dstDoc.getFirstSection().getBody().removeAllChildren();

	// Importujte každý uzel ze seznamu do nového dokumentu. Originál si ponechte
	// formátování uzlu.
	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);
	}

	// Vraťte vygenerovaný dokument.
	return dstDoc;
}

Nyní jsme připraveni použít tyto metody a extrahovat text z dokumentu aplikace Word.

Extrahujte text mezi odstavci dokumentu aplikace Word

Podívejme se, jak extrahovat obsah mezi dvěma odstavci v dokumentu Word DOCX. Následují kroky k provedení této operace v Javě.

  • Nejprve načtěte dokument aplikace Word pomocí třídy Document.
  • Pomocí metody Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool) získáte odkaz na počáteční a koncový odstavce do dvou objektů.
  • Voláním metody extractContent(startPara, endPara, true) extrahujte uzly do objektu.
  • Chcete-li vytvořit dokument skládající se z extrahovaného obsahu, zavolejte pomocnou metodu createDocument(Document, extractNodes).
  • Nakonec uložte vrácený dokument pomocí metody Document.save(String).

Následující ukázka kódu ukazuje, jak extrahovat text mezi 7. a 11. odstavcem ve Word DOCX v Javě.

// Načíst dokument
Document doc = new Document("TestFile.doc");

// Shromážděte uzly. Metoda GetChild používá index založený na 0
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// Extrahujte obsah mezi těmito uzly v dokumentu. Zahrňte tyto
// markery v extrakci.
ArrayList extractedNodes = extractContent(startPara, endPara, true);

// Vložte obsah do nového samostatného dokumentu a uložte jej na disk.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Extrahovat text mezi různými uzly dokumentu Word

Můžete také extrahovat obsah mezi různými typy uzlů. Pro demonstraci vyjmeme obsah mezi odstavcem a tabulkou a uložíme jej do nového dokumentu aplikace Word. Následují kroky k extrahování textu mezi různými uzly v dokumentu aplikace Word v Javě.

  • Načtěte dokument aplikace Word pomocí třídy Document.
  • Získejte odkaz na počáteční a koncový uzel do dvou objektů pomocí metody Document.getFirstSection().getChild(NodeType, int, bool).
  • Voláním metody extractContent(startPara, endPara, true) extrahujte uzly do objektu.
  • Chcete-li vytvořit dokument skládající se z extrahovaného obsahu, zavolejte pomocnou metodu createDocument(Document, extractNodes).
  • Uložte vrácený dokument pomocí metody Document.save(String).

Následující ukázka kódu ukazuje, jak extrahovat text mezi odstavcem a tabulkou ve Wordu DOCX pomocí Java.

// Vložte dokumenty
Document doc = new Document("TestFile.doc");

// Získejte odkaz na úvodní odstavec
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);

// Extrahujte obsah mezi těmito uzly v dokumentu. Zahrňte tyto značky do extrakce.
ArrayList extractedNodes = extractContent(startPara, endTable, true);

// Umožňuje obrátit pole, aby bylo vkládání obsahu zpět do dokumentu snazší.
Collections.reverse(extractedNodes);

while (extractedNodes.size() > 0) {
    // Vložte poslední uzel z obráceného seznamu
    endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
    // Po vložení tento uzel odeberte ze seznamu.
    extractedNodes.remove(0);
}

// Uložte vygenerovaný dokument na disk.
doc.save("output.doc");

Získejte text mezi odstavci na základě stylů

Pojďme se nyní podívat, jak extrahovat obsah mezi odstavci na základě stylů. Pro demonstraci se chystáme extrahovat obsah mezi prvním „Nadpisem 1“ a prvním „Nadpisem 3“ v dokumentu Word. Následující kroky ukazují, jak toho dosáhnout v Javě.

  • Nejprve načtěte dokument aplikace Word pomocí třídy Document.
  • Potom extrahujte odstavce do objektu pomocí pomocné metody odstavceByStyleName(Dokument, “Nadpis 1”).
  • Extrahujte odstavce do jiného objektu pomocí pomocné metody pointsByStyleName(Document, “Nadpis 3”).
  • Zavolejte metodu extractContent(startPara, endPara, true) a předejte první prvky v obou polích odstavců jako první a druhý parametr.
  • Chcete-li vytvořit dokument skládající se z extrahovaného obsahu, zavolejte pomocnou metodu createDocument(Document, extractNodes).
  • Nakonec uložte vrácený dokument pomocí metody Document.save(String).

Následující ukázka kódu ukazuje, jak extrahovat obsah mezi odstavci na základě stylů.

// Načíst dokument
Document doc = new Document(dataDir + "TestFile.doc");

// Shromážděte seznam odstavců pomocí příslušných stylů nadpisů.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");

// Použijte první výskyt odstavců s těmito styly.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);

// Extrahujte obsah mezi těmito uzly v dokumentu. Nezahrnujte tyto značky do extrakce.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);

// Vložte obsah do nového samostatného dokumentu a uložte jej na disk.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Zdarma Word Text Extractor pro Java

Můžete získat bezplatnou dočasnou licenci k extrahování textu z dokumentů aplikace Word bez omezení hodnocení.

Prozkoumejte Java DOCX Library

Další scénáře extrahování textu z dokumentů aplikace Word můžete prozkoumat pomocí tohoto článku dokumentace. Kromě toho můžete prozkoumat další funkce Aspose.Words pro Java pomocí dokumentace. V případě jakýchkoli dotazů nás neváhejte kontaktovat prostřednictvím našeho fóra.

Závěr

V tomto článku jste se naučili, jak extrahovat text z dokumentů aplikace Word v Javě. Navíc jste viděli, jak programově extrahovat obsah mezi podobnými nebo různými typy uzlů v DOC nebo DOCX. Můžete si tak vytvořit svůj vlastní textový extraktor MS Word v Javě.

Viz také