Extraire du texte de documents MS Word en Java

L’extraction de texte à partir de documents Word est souvent effectuée dans différents scénarios. Par exemple, pour analyser le texte, pour extraire des sections particulières d’un document et les combiner en un seul document, etc. Dans cet article, vous apprendrez à extraire du texte de documents Word par programmation en Java. De plus, nous verrons comment extraire dynamiquement du contenu entre des éléments spécifiques tels que des paragraphes, des tableaux, etc.

Bibliothèque Java pour extraire du texte de documents Word

Aspose.Words for Java est une bibliothèque puissante qui vous permet de créer des documents MS Word à partir de zéro. De plus, il vous permet de manipuler les documents Word existants pour le cryptage, la conversion, l’extraction de texte, etc. Nous utiliserons cette bibliothèque pour extraire le texte des documents Word DOCX ou DOC. Vous pouvez télécharger le JAR de l’API ou l’installer à l’aide des configurations Maven suivantes.

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

Extraction de texte dans Word DOC/DOCX en Java

Un document MS Word se compose de divers éléments, notamment des paragraphes, des tableaux, des images, etc. Par conséquent, les exigences d’extraction de texte peuvent varier d’un scénario à l’autre. Par exemple, vous devrez peut-être extraire du texte entre des paragraphes, des signets, des commentaires, etc.

Chaque type d’élément dans un Word DOC/DOCX est représenté comme un nœud. Par conséquent, pour traiter un document, vous devrez jouer avec les nœuds. Commençons donc et voyons comment extraire du texte de documents Word dans différents scénarios.

Extraire le texte d’un Word DOC en Java

Dans cette section, nous allons implémenter un extracteur de texte Java pour les documents Word et le flux de travail d’extraction de texte serait le suivant :

  • Tout d’abord, nous définirons les nœuds que nous souhaitons inclure dans le processus d’extraction de texte.
  • Ensuite, nous extrairons le contenu entre les nœuds spécifiés (y compris ou en excluant les nœuds de début et de fin).
  • Enfin, nous utiliserons le clone des nœuds extraits, par exemple pour créer un nouveau document Word composé du contenu extrait.

Écrivons maintenant une méthode nommée extractContent à laquelle nous allons passer les nœuds et quelques autres paramètres pour effectuer l’extraction de texte. Cette méthode analysera le document et clonera les nœuds. Voici les paramètres que nous allons passer à cette méthode.

  1. startNode et endNode comme points de départ et d’arrivée pour l’extraction du contenu, respectivement. Il peut s’agir de nœuds de niveau bloc (Paragraph , Table) ou de niveau inline (par exemple, Run, FieldStart, BookmarkStart, etc.).
    1. Pour transmettre un champ, vous devez transmettre l’objet FieldStart correspondant.
    2. Pour transmettre des signets, les nœuds BookmarkStart et BookmarkEnd doivent être transmis.
    3. Pour les commentaires, les nœuds CommentRangeStart et CommentRangeEnd doivent être utilisés.
  2. isInclusive définit si les marqueurs sont inclus ou non dans l’extraction. Si cette option est définie sur false et que le même nœud ou des nœuds consécutifs sont passés, une liste vide sera renvoyée.

Ce qui suit est l’implémentation complète de la méthode extractContent qui extrait le contenu entre les nœuds qui sont passés.

// Pour des exemples complets et des fichiers de données, rendez-vous sur https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
    // Vérifiez d'abord que les nœuds passés à cette méthode sont valides pour être utilisés.
    verifyParameterNodes(startNode, endNode);

    // Créez une liste pour stocker les nœuds extraits.
    ArrayList nodes = new ArrayList();

    // Conservez un enregistrement des nœuds d'origine transmis à cette méthode afin que nous puissions diviser les nœuds marqueurs si nécessaire.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Extrayez le contenu en fonction des nœuds de niveau bloc (paragraphes et tableaux). Parcourez les nœuds parents pour les trouver.
    // Nous diviserons le contenu des premier et dernier nœuds selon que les nœuds marqueurs sont en ligne
    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;
    // Le nœud actuel que nous extrayons du document.
    Node currNode = startNode;

    // Commencez à extraire le contenu. Traitez tous les nœuds de niveau bloc et divisez spécifiquement les premier et dernier nœuds si nécessaire afin que la mise en forme des paragraphes soit conservée.
    // La méthode est un peu plus complexe qu'un extracteur ordinaire car nous devons prendre en compte l'extraction à l'aide de nœuds en ligne, de champs, de signets, etc. pour la rendre vraiment utile.
    while (isExtracting) {
        // Clonez le nœud actuel et ses enfants pour obtenir une copie.
        /*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) {
            // Nous devons traiter chaque marqueur séparément, alors passez-le plutôt à une méthode distincte.
            if (isStartingNode) {
                processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // Le conditionnel doit être séparé car les marqueurs de début et de fin au niveau du bloc peuvent être le même nœud.
            if (isEndingNode) {
                processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        } else
            // Le nœud n'est pas un marqueur de début ou de fin, ajoutez simplement la copie à la liste.
            nodes.add(cloneNode);

        // Passez au nœud suivant et extrayez-le. Si le nœud suivant est nul, cela signifie que le reste du contenu se trouve dans une section différente.
        if (currNode.getNextSibling() == null && isExtracting) {
            // Passez à la section suivante.
            Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
            currNode = nextSection.getBody().getFirstChild();
        } else {
            // Passez au nœud suivant dans le corps.
            currNode = currNode.getNextSibling();
        }
    }

    // Renvoyez les nœuds entre les marqueurs de nœud.
    return nodes;
}

Certaines méthodes d’assistance sont également requises par la méthode extractContent pour accomplir l’opération d’extraction de texte, qui sont indiquées ci-dessous.

/**
 * Vérifie que les paramètres d'entrée sont corrects et utilisables. Lève une exception
 * s'il y a un problème.
 */
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
	// L'ordre dans lequel ces contrôles sont effectués est important.
	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");

	// Vérifiez que le nœud de fin est après le nœud de début dans l'arborescence DOM
	// Vérifiez d'abord s'ils se trouvent dans des sections différentes, puis s'ils ne le sont pas, vérifiez
	// leur position dans le corps de la même section où ils se trouvent.
	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");
}

/**
 * Vérifie si un nœud passé est un nœud en ligne.
 */
private static boolean isInline(Node node) throws Exception {
	// Teste si le nœud est descendant d'un nœud Paragraphe ou Tableau et n'est pas non plus un
	// paragraphe ou un tableau un paragraphe à l'intérieur d'une classe de commentaires qui est le descendant de
	// un paragraphe est possible.
	return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
			&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}

/**
 * Supprime le contenu avant ou après le marqueur dans le nœud cloné en fonction
 * sur le type de marqueur.
 */
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
		boolean isStartMarker, boolean isEndMarker) throws Exception {
	// Si nous avons affaire à un nœud de niveau bloc, il suffit de voir s'il doit être inclus
	// et ajoutez-le à la liste.
	if (!isInline(node)) {
		// N'ajoutez pas le nœud deux fois si les marqueurs sont le même nœud
		if (!(isStartMarker && isEndMarker)) {
			if (isInclusive)
				nodes.add(cloneNode);
		}
		return;
	}

	// Si un marqueur est un nœud FieldStart, vérifiez s'il doit être inclus ou non.
	// Nous supposons pour simplifier que FieldStart et FieldEnd apparaissent dans le même
	// paragraphe.
	if (node.getNodeType() == NodeType.FIELD_START) {
		// Si le marqueur est un nœud de départ et n'est pas inclus, passez à la fin de
		// le champ.
		// Si le marqueur est un nœud de fin et qu'il doit être inclus, déplacez-vous vers la fin
		// champ afin que le champ ne soit pas supprimé.
		if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
			while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
				node = node.getNextSibling();

		}
	}

	// Si l'un ou l'autre des marqueurs fait partie d'un commentaire, pour inclure le commentaire lui-même, nous
	// besoin de déplacer le pointeur vers le commentaire
	// nœud trouvé après le nœud CommentRangeEnd.
	if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
		while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
			node = node.getNextSibling();

	}

	// Trouvez le nœud correspondant dans notre nœud cloné par index et renvoyez-le.
	// Si le nœud de début et de fin sont identiques, certains nœuds enfants peuvent déjà avoir
	// été supprimée. Soustraire le
	// différence pour obtenir le bon indice.
	int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();

	// Nombre de nœuds enfants identique.
	if (indexDiff == 0)
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
	else
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);

	// Supprimez les nœuds jusqu'au/du marqueur.
	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();
	}

	// Après le traitement, le nœud composite peut devenir vide. Si c'est le cas, n'incluez pas
	// ce.
	if (!(isStartMarker && isEndMarker)) {
		if (cloneNode.hasChildNodes())
			nodes.add(cloneNode);
	}
}

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

	// Créez un document vierge.
	Document dstDoc = new Document();
	// Supprimez le premier paragraphe du document vide.
	dstDoc.getFirstSection().getBody().removeAllChildren();

	// Importez chaque nœud de la liste dans le nouveau document. Gardez l'original
	// formatage du nœud.
	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);
	}

	// Renvoie le document généré.
	return dstDoc;
}

Nous sommes maintenant prêts à utiliser ces méthodes et à extraire le texte d’un document Word.

Java Extraire le texte entre les paragraphes dans un Word DOC

Voyons comment extraire le contenu entre deux paragraphes dans un document Word DOCX. Voici les étapes pour effectuer cette opération en Java.

  • Tout d’abord, chargez le document Word à l’aide de la classe Document.
  • Obtenez la référence des paragraphes de début et de fin dans deux objets à l’aide de la méthode Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool).
  • Appelez la méthode extractContent(startPara, endPara, true) pour extraire les nœuds dans un objet.
  • Appelez la méthode d’assistance generateDocument(Document, extractNodes) pour créer un document composé du contenu extrait.
  • Enfin, enregistrez le document renvoyé à l’aide de la méthode Document.save(String).

L’exemple de code suivant montre comment extraire du texte entre les 7e et 11e paragraphes dans un Word DOCX en Java.

// Charger le document
Document doc = new Document("TestFile.doc");

// Rassemblez les nœuds. La méthode GetChild utilise un index basé sur 0
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// Extrayez le contenu entre ces nœuds dans le document. Inclure ces
// marqueurs dans l'extraction.
ArrayList extractedNodes = extractContent(startPara, endPara, true);

// Insérez le contenu dans un nouveau document séparé et enregistrez-le sur le disque.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Java Extraire le texte du DOC - Entre différents types de nœuds

Vous pouvez également extraire du contenu entre différents types de nœuds. Pour la démonstration, extrayons le contenu entre un paragraphe et un tableau et enregistrons-le dans un nouveau document Word. Voici les étapes pour extraire du texte entre différents nœuds dans un document Word en Java.

  • Chargez le document Word à l’aide de la classe Document.
  • Obtenez la référence des nœuds de début et de fin dans deux objets à l’aide de la méthode Document.getFirstSection().getChild(NodeType, int, bool).
  • Appelez la méthode extractContent(startPara, endPara, true) pour extraire les nœuds dans un objet.
  • Appelez la méthode d’assistance generateDocument(Document, extractNodes) pour créer un document composé du contenu extrait.
  • Enregistrez le document renvoyé à l’aide de la méthode Document.save(String).

L’exemple de code suivant montre comment extraire du texte entre un paragraphe et un tableau dans un DOCX à l’aide de Java.

// Charger des documents
Document doc = new Document("TestFile.doc");

// Obtenir la référence du paragraphe de départ
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);

// Extrayez le contenu entre ces nœuds dans le document. Inclure ces marqueurs dans l'extraction.
ArrayList extractedNodes = extractContent(startPara, endTable, true);

// Inversons le tableau pour faciliter la réinsertion du contenu dans le document.
Collections.reverse(extractedNodes);

while (extractedNodes.size() > 0) {
    // Insérer le dernier nœud de la liste inversée
    endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
    // Supprimez ce nœud de la liste après l'insertion.
    extractedNodes.remove(0);
}

// Enregistrez le document généré sur le disque.
doc.save("output.doc");

Java Extraction de texte à partir de DOCX - Entre les paragraphes en fonction des styles

Voyons maintenant comment extraire le contenu entre les paragraphes en fonction des styles. Pour la démonstration, nous allons extraire le contenu entre le premier “Titre 1” et le premier “Titre 3” dans le document Word. Les étapes suivantes montrent comment y parvenir en Java.

  • Tout d’abord, chargez le document Word à l’aide de la classe Document.
  • Ensuite, extrayez les paragraphes dans un objet à l’aide de la méthode d’assistance paragraphesByStyleName(Document, “Heading 1”).
  • Extrayez des paragraphes dans un autre objet à l’aide de la méthode d’assistance paragraphesByStyleName(Document, “Heading 3”).
  • Appelez la méthode extractContent(startPara, endPara, true) et transmettez les premiers éléments des deux tableaux de paragraphes en tant que premier et deuxième paramètres.
  • Appelez la méthode d’assistance generateDocument(Document, extractNodes) pour créer un document composé du contenu extrait.
  • Enfin, enregistrez le document renvoyé à l’aide de la méthode Document.save(String).

L’exemple de code suivant montre comment extraire le contenu entre les paragraphes en fonction des styles.

// Charger le document
Document doc = new Document(dataDir + "TestFile.doc");

// Rassemblez une liste des paragraphes en utilisant les styles de titre respectifs.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");

// Utilisez la première instance des paragraphes avec ces styles.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);

// Extrayez le contenu entre ces nœuds dans le document. N'incluez pas ces marqueurs dans l'extraction.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);

// Insérez le contenu dans un nouveau document séparé et enregistrez-le sur le disque.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Extracteur de texte Java Word - En savoir plus

Vous pouvez explorer d’autres scénarios d’extraction de texte à partir de documents Word à l’aide de cet article de documentation.

API Java pour extraire du texte de DOC/DOCX - Obtenez une licence gratuite

Vous pouvez obtenir une licence temporaire pour utiliser Aspose.Words for Java sans limitation d’évaluation.

Conclusion

Dans cet article, vous avez appris à extraire du texte de MS Word DOC DOCX en Java. De plus, vous avez vu comment extraire par programmation du contenu entre des types de nœuds similaires ou différents dans un document Word. Ainsi, vous pouvez créer votre propre extracteur de texte MS Word en Java. En outre, vous pouvez explorer d’autres fonctionnalités d’Aspose.Words for Java à l’aide de la documentation. Si vous avez des questions, n’hésitez pas à nous en faire part via notre forum.

Voir également