Estrai testo da documenti MS Word in Java

L’estrazione del testo dai documenti di Word viene spesso eseguita in diversi scenari. Ad esempio, per analizzare il testo, per estrarre particolari sezioni di un documento e combinarle in un unico documento, e così via. In questo articolo imparerai come estrarre testo da documenti Word in modo programmatico in Java. Inoltre, tratteremo come estrarre il contenuto tra elementi specifici come paragrafi, tabelle, ecc. in modo dinamico.

Libreria Java per estrarre testo da documenti Word

Aspose.Words for Java è una potente libreria che ti consente di creare documenti MS Word da zero. Inoltre, ti consente di manipolare i documenti Word esistenti per la crittografia, la conversione, l’estrazione di testo, ecc. Useremo questa libreria per estrarre il testo dai documenti Word DOCX o DOC. Puoi scaricare il JAR dell’API o installarlo utilizzando le seguenti configurazioni 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>

Estrazione di testo in Word DOC/DOCX in Java

Un documento MS Word è costituito da vari elementi, che includono paragrafi, tabelle, immagini, ecc. Pertanto, i requisiti per l’estrazione del testo possono variare da uno scenario all’altro. Ad esempio, potrebbe essere necessario estrarre il testo tra paragrafi, segnalibri, commenti, ecc.

Ogni tipo di elemento in una Word DOC/DOCX è rappresentato come un nodo. Pertanto, per elaborare un documento, dovrai giocare con i nodi. Quindi iniziamo e vediamo come estrarre testo da documenti Word in diversi scenari.

Estrai testo da un documento Word in Java

In questa sezione, implementeremo un estrattore di testo Java per documenti Word e il flusso di lavoro dell’estrazione del testo sarebbe il seguente:

  • In primo luogo, definiremo i nodi che vogliamo includere nel processo di estrazione del testo.
  • Quindi, estrarremo il contenuto tra i nodi specificati (includendo o escludendo i nodi iniziali e finali).
  • Infine, utilizzeremo il clone dei nodi estratti, ad esempio per creare un nuovo documento Word composto da contenuto estratto.

Scriviamo ora un metodo chiamato extractContent a cui passeremo i nodi e alcuni altri parametri per eseguire l’estrazione del testo. Questo metodo analizzerà il documento e clonerà i nodi. Di seguito sono riportati i parametri che passeremo a questo metodo.

  1. startNode ed endNode rispettivamente come punti di inizio e fine per l’estrazione del contenuto. Questi possono essere nodi a livello di blocco (Paragraph, Table) o inline (ad es. Run, FieldStart, BookmarkStart ecc.).
    1. Per passare un campo devi passare l’oggetto FieldStart corrispondente.
    2. Per passare i segnalibri, è necessario passare i nodi BookmarkStart e BookmarkEnd.
    3. Per i commenti, è necessario utilizzare i nodi CommentRangeStart e CommentRangeEnd.
  2. isInclusive definisce se i marker sono inclusi nell’estrazione o meno. Se questa opzione è impostata su false e vengono passati lo stesso nodo o nodi consecutivi, verrà restituito un elenco vuoto.

Quella che segue è l’implementazione completa del metodo extractContent che estrae il contenuto tra i nodi che vengono passati.

// Per esempi completi e file di dati, vai a https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
    // Innanzitutto verificare che i nodi passati a questo metodo siano validi per l'uso.
    verifyParameterNodes(startNode, endNode);

    // Crea un elenco per memorizzare i nodi estratti.
    ArrayList nodes = new ArrayList();

    // Conserva un record dei nodi originali passati a questo metodo in modo da poter dividere i nodi marker se necessario.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Estrarre il contenuto in base ai nodi a livello di blocco (paragrafi e tabelle). Attraversa i nodi principali per trovarli.
    // Divideremo il contenuto del primo e dell'ultimo nodo a seconda che i nodi marker siano in linea
    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;
    // Il nodo corrente che stiamo estraendo dal documento.
    Node currNode = startNode;

    // Inizia a estrarre il contenuto. Elabora tutti i nodi a livello di blocco e dividi in modo specifico il primo e l'ultimo nodo quando necessario, in modo da mantenere la formattazione del paragrafo.
    // Il metodo è leggermente più complesso di un normale estrattore in quanto è necessario tenere conto dell'estrazione utilizzando nodi, campi, segnalibri, ecc. Inline per renderlo davvero utile.
    while (isExtracting) {
        // Clona il nodo corrente e i suoi figli per ottenere una copia.
        /*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) {
            // Abbiamo bisogno di elaborare ogni marcatore separatamente, quindi passalo invece a un metodo separato.
            if (isStartingNode) {
                processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // Conditional deve essere separato poiché i marker di inizio e fine a livello di blocco potrebbero essere lo stesso nodo.
            if (isEndingNode) {
                processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        } else
            // Il nodo non è un indicatore di inizio o fine, è sufficiente aggiungere la copia all'elenco.
            nodes.add(cloneNode);

        // Passa al nodo successivo ed estrailo. Se il nodo successivo è nullo significa che il resto del contenuto si trova in una sezione diversa.
        if (currNode.getNextSibling() == null && isExtracting) {
            // Passa alla sezione successiva.
            Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
            currNode = nextSection.getBody().getFirstChild();
        } else {
            // Passa al nodo successivo nel corpo.
            currNode = currNode.getNextSibling();
        }
    }

    // Restituisce i nodi tra gli indicatori di nodo.
    return nodes;
}

Alcuni metodi di supporto sono richiesti anche dal metodo extractContent per eseguire l’operazione di estrazione del testo, come indicato di seguito.

/**
 * Verifica che i parametri di input siano corretti e possano essere utilizzati. Genera un'eccezione
 * se c'è qualche problema.
 */
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
	// L'ordine in cui vengono eseguiti questi controlli è importante.
	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");

	// Verificare che il nodo finale sia dopo il nodo iniziale nell'albero DOM
	// Per prima cosa controlla se si trovano in sezioni diverse, quindi se non lo sono
	// la loro posizione nel corpo della stessa sezione in cui si trovano.
	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");
}

/**
 * Verifica se un nodo passato è un nodo inline.
 */
private static boolean isInline(Node node) throws Exception {
	// Verifica se il nodo è deendant di un nodo Paragrafo o Tabella e anche non è a
	// paragrafo o tabella un paragrafo all'interno di una classe di commento discendente di
	// è possibile una parafrasi.
	return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
			&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}

/**
 * Rimuove il contenuto prima o dopo il marcatore nel nodo clonato a seconda
 * sul tipo di marcatore.
 */
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
		boolean isStartMarker, boolean isEndMarker) throws Exception {
	// Se abbiamo a che fare con un nodo a livello di blocco, controlla se dovrebbe essere incluso
	// e aggiungilo alla lista.
	if (!isInline(node)) {
		// Non aggiungere il nodo due volte se i marcatori sono lo stesso nodo
		if (!(isStartMarker && isEndMarker)) {
			if (isInclusive)
				nodes.add(cloneNode);
		}
		return;
	}

	// Se un marker è un nodo FieldStart, controlla se deve essere incluso o meno.
	// Assumiamo per semplicità che FieldStart e FieldEnd appaiano nello stesso modo
	// paragrafo.
	if (node.getNodeType() == NodeType.FIELD_START) {
		// Se il marker è un nodo iniziale e non viene incluso, salta alla fine di
		// il campo.
		// Se il marker è un nodo finale e deve essere incluso, spostati alla fine
		// campo in modo che il campo non venga rimosso.
		if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
			while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
				node = node.getNextSibling();

		}
	}

	// Se uno dei marcatori fa parte di un commento, allora per includere il commento stesso noi
	// necessario spostare il puntatore in avanti al Commento
	// nodo trovato dopo il nodo CommentRangeEnd.
	if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
		while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
			node = node.getNextSibling();

	}

	// Trova il nodo corrispondente nel nostro nodo clonato per indice e restituiscilo.
	// Se il nodo iniziale e finale sono gli stessi, alcuni nodi figlio potrebbero già avere
	// stato rimosso. Sottrarre il
	// differenza per ottenere l'indice corretto.
	int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();

	// Conteggio dei nodi figlio identico.
	if (indexDiff == 0)
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
	else
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);

	// Rimuovere i nodi fino a/dal marcatore.
	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();
	}

	// Dopo l'elaborazione, il nodo composito potrebbe diventare vuoto. Se ha non includere
	// esso.
	if (!(isStartMarker && isEndMarker)) {
		if (cloneNode.hasChildNodes())
			nodes.add(cloneNode);
	}
}

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

	// Crea un documento vuoto.
	Document dstDoc = new Document();
	// Rimuovere il primo paragrafo dal documento vuoto.
	dstDoc.getFirstSection().getBody().removeAllChildren();

	// Importa ogni nodo dall'elenco nel nuovo documento. Conserva l'originale
	// formattazione del nodo.
	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);
	}

	// Restituire il documento generato.
	return dstDoc;
}

Ora siamo pronti per utilizzare questi metodi ed estrarre il testo da un documento di Word.

Java Estrai il testo tra i paragrafi in un documento Word

Vediamo come estrarre il contenuto tra due paragrafi in un documento Word DOCX. Di seguito sono riportati i passaggi per eseguire questa operazione in Java.

  • Innanzitutto, carica il documento di Word utilizzando la classe Document.
  • Ottieni il riferimento dei paragrafi iniziale e finale in due oggetti usando il metodo Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool).
  • Chiama il metodo extractContent(startPara, endPara, true) per estrarre i nodi in un oggetto.
  • Chiama il metodo helper generateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Infine, salva il documento restituito usando il metodo Document.save(String).

L’esempio di codice seguente mostra come estrarre il testo tra il settimo e l’undicesimo paragrafo in un Word DOCX in Java.

// Carica documento
Document doc = new Document("TestFile.doc");

// Raccogli i nodi. Il metodo GetChild usa l'indice basato su 0
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// Estrarre il contenuto tra questi nodi nel documento. Includi questi
// marcatori nell'estrazione.
ArrayList extractedNodes = extractContent(startPara, endPara, true);

// Inserisci il contenuto in un nuovo documento separato e salvalo su disco.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Java Estrai testo da DOC - Tra diversi tipi di nodi

Puoi anche estrarre contenuto tra diversi tipi di nodi. A scopo dimostrativo, estraiamo il contenuto tra un paragrafo e una tabella e lo salviamo in un nuovo documento di Word. Di seguito sono riportati i passaggi per estrarre il testo tra diversi nodi in un documento Word in Java.

  • Carica il documento di Word usando la classe Document.
  • Ottieni il riferimento dei nodi iniziali e finali in due oggetti utilizzando il metodo Document.getFirstSection().getChild(NodeType, int, bool).
  • Chiama il metodo extractContent(startPara, endPara, true) per estrarre i nodi in un oggetto.
  • Chiama il metodo helper generateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Salvare il documento restituito utilizzando il metodo Document.save(String).

L’esempio di codice seguente mostra come estrarre il testo tra un paragrafo e una tabella in un DOCX utilizzando Java.

// Carica documenti
Document doc = new Document("TestFile.doc");

// Ottieni il riferimento del paragrafo iniziale
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);

// Estrarre il contenuto tra questi nodi nel documento. Includere questi marcatori nell'estrazione.
ArrayList extractedNodes = extractContent(startPara, endTable, true);

// Consente di invertire l'array per facilitare l'inserimento del contenuto nel documento.
Collections.reverse(extractedNodes);

while (extractedNodes.size() > 0) {
    // Inserire l'ultimo nodo dall'elenco invertito
    endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
    // Rimuovere questo nodo dall'elenco dopo l'inserimento.
    extractedNodes.remove(0);
}

// Salva il documento generato su disco.
doc.save("output.doc");

Java Estrazione di testo da DOCX - Tra paragrafi basati su stili

Diamo ora un’occhiata a come estrarre il contenuto tra i paragrafi in base agli stili. A scopo dimostrativo, estrarremo il contenuto tra il primo “Titolo 1” e il primo “Titolo 3” nel documento di Word. I seguenti passaggi mostrano come ottenere ciò in Java.

  • Innanzitutto, carica il documento di Word utilizzando la classe Document.
  • Quindi, estrai i paragrafi in un oggetto usando il metodo di supporto paragrafiPerNomeStile(Documento, “Intestazione 1”).
  • Estrarre i paragrafi in un altro oggetto utilizzando il metodo di supporto paragrafiPerNomeStile(Documento, “Intestazione 3”).
  • Chiama il metodo extractContent(startPara, endPara, true) e passa i primi elementi in entrambe le matrici di paragrafi come primo e secondo parametro.
  • Chiama il metodo helper generateDocument(Document, extractNodes) per creare un documento costituito dal contenuto estratto.
  • Infine, salva il documento restituito usando il metodo Document.save(String).

Nell’esempio di codice seguente viene illustrato come estrarre il contenuto tra i paragrafi in base agli stili.

// Carica documento
Document doc = new Document(dataDir + "TestFile.doc");

// Raccogli un elenco dei paragrafi utilizzando i rispettivi stili di intestazione.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");

// Usa la prima istanza dei paragrafi con quegli stili.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);

// Estrarre il contenuto tra questi nodi nel documento. Non includere questi marcatori nell'estrazione.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);

// Inserisci il contenuto in un nuovo documento separato e salvalo su disco.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Estrattore di testo di Word Java - Ulteriori informazioni

Puoi esplorare altri scenari di estrazione di testo da documenti Word usando questo articolo della documentazione.

API Java per estrarre testo da DOC/DOCX - Ottieni una licenza gratuita

Puoi ottenere una licenza temporanea per utilizzare Aspose.Words per Java senza limitazioni di valutazione.

Conclusione

In questo articolo, hai imparato come estrarre il testo da MS Word DOC DOCX in Java. Inoltre, hai visto come estrarre il contenuto tra tipi simili o diversi di nodi in un documento di Word a livello di codice. Pertanto, puoi creare il tuo estrattore di testo MS Word in Java. Inoltre, puoi esplorare altre funzionalità di Aspose.Words per Java usando la documentazione. In caso di domande, non esitare a farcelo sapere tramite il nostro forum.

Guarda anche