Extraire du texte de documents MS Word en C#

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 C#. De plus, nous verrons comment extraire dynamiquement du contenu entre des éléments spécifiques tels que des paragraphes, des tableaux, etc.

Bibliothèque C# pour extraire du texte de documents Word

Aspose.Words for .NET 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 la DLL de l’API ou l’installer directement à partir de NuGet à l’aide de la console du gestionnaire de packages.

PM> Install-Package Aspose.Words

Extraction de texte dans des documents Word à l’aide de C#

Un document MS Word se compose de divers éléments qui incluent 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 document Word est représenté sous la forme d’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 document Word en C#

Dans cette section, nous allons implémenter un extracteur de texte C# pour les documents Word et le workflow 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.

public static ArrayList ExtractContent(Node startNode, Node endNode, bool isInclusive)
{
    // 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.ParentNode.NodeType != NodeType.Body)
        startNode = startNode.ParentNode;

    while (endNode.ParentNode.NodeType != NodeType.Body)
        endNode = endNode.ParentNode;

    bool isExtracting = true;
    bool isStartingNode = true;
    bool isEndingNode = false;
    // 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.
        Node cloneNode = currNode.Clone(true);
        isEndingNode = currNode.Equals(endNode);

        if ((isStartingNode || isEndingNode) && cloneNode.IsComposite)
        {
            // Nous devons traiter chaque marqueur séparément, alors passez-le plutôt à une méthode distincte.
            if (isStartingNode)
            {
                ProcessMarker((CompositeNode)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((CompositeNode)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.NextSibling == null && isExtracting)
        {
            // Passez à la section suivante.
            Section nextSection = (Section)currNode.GetAncestor(NodeType.Section).NextSibling;
            currNode = nextSection.Body.FirstChild;
        }
        else
        {
            // Passez au nœud suivant dans le corps.
            currNode = currNode.NextSibling;
        }
    }

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

public static List<Paragraph> ParagraphsByStyleName(Document doc, string styleName)
{
    // Créez un tableau pour collecter les paragraphes du style spécifié.
    List<Paragraph> paragraphsWithStyle = new List<Paragraph>();

    NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);

    // Parcourez tous les paragraphes pour trouver ceux qui ont le style spécifié.
    foreach (Paragraph paragraph in paragraphs)
    {
        if (paragraph.ParagraphFormat.Style.Name == styleName)
            paragraphsWithStyle.Add(paragraph);
    }

    return paragraphsWithStyle;
}
private static void VerifyParameterNodes(Node startNode, Node endNode)
{
    // L'ordre dans lequel ces contrôles sont effectués est important.
    if (startNode == null)
        throw new ArgumentException("Start node cannot be null");
    if (endNode == null)
        throw new ArgumentException("End node cannot be null");

    if (!startNode.Document.Equals(endNode.Document))
        throw new ArgumentException("Start node and end node must belong to the same document");

    if (startNode.GetAncestor(NodeType.Body) == null || endNode.GetAncestor(NodeType.Body) == null)
        throw new ArgumentException("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 dans laquelle ils se trouvent.
    Section startSection = (Section)startNode.GetAncestor(NodeType.Section);
    Section endSection = (Section)endNode.GetAncestor(NodeType.Section);

    int startIndex = startSection.ParentNode.IndexOf(startSection);
    int endIndex = endSection.ParentNode.IndexOf(endSection);

    if (startIndex == endIndex)
    {
        if (startSection.Body.IndexOf(startNode) > endSection.Body.IndexOf(endNode))
            throw new ArgumentException("The end node must be after the start node in the body");
    }
    else if (startIndex > endIndex)
        throw new ArgumentException("The section of end node must be after the section start node");
}
private static bool IsInline(Node node)
{
    // Tester si le nœud est le descendant d'un nœud Paragraphe ou Table et n'est pas non plus un paragraphe ou un tableau. Un paragraphe à l'intérieur d'une classe de commentaire qui est descendant d'un paragraphe est possible.
    return ((node.GetAncestor(NodeType.Paragraph) != null || node.GetAncestor(NodeType.Table) != null) && !(node.NodeType == NodeType.Paragraph || node.NodeType == NodeType.Table));
}
private static void ProcessMarker(CompositeNode cloneNode, ArrayList nodes, Node node, bool isInclusive, bool isStartMarker, bool isEndMarker)
{
    // Si nous avons affaire à un nœud de niveau bloc, voyez simplement 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.NodeType == NodeType.FieldStart)
    {
        // Si le marqueur est un nœud de départ et n'est pas inclus, passez à la fin du champ.
        // Si le marqueur est un nœud de fin et qu'il doit être inclus, déplacez-vous vers le champ de fin afin que le champ ne soit pas supprimé.
        if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive))
        {
            while (node.NextSibling != null && node.NodeType != NodeType.FieldEnd)
                node = node.NextSibling;

        }
    }

    // Si l'un des marqueurs fait partie d'un commentaire, pour inclure le commentaire lui-même, nous devons déplacer le pointeur vers le commentaire
    // Nœud trouvé après le nœud CommentRangeEnd.
    if (node.NodeType == NodeType.CommentRangeEnd)
    {
        while (node.NextSibling != null && node.NodeType != NodeType.Comment)
            node = node.NextSibling;

    }

    // 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 ont peut-être déjà été supprimés. Soustraire le
    // Différence pour obtenir le bon index.
    int indexDiff = node.ParentNode.ChildNodes.Count - cloneNode.ChildNodes.Count;

    // Nombre de nœuds enfants identique.
    if (indexDiff == 0)
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node)];
    else
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node) - indexDiff];

    // Supprimez les nœuds jusqu'au/du marqueur.
    bool isSkip = false;
    bool isProcessing = true;
    bool isRemoving = isStartMarker;
    Node nextNode = cloneNode.FirstChild;

    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.NextSibling;
        if (isRemoving && !isSkip)
            currentNode.Remove();
    }

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

}
public static Document GenerateDocument(Document srcDoc, ArrayList nodes)
{
    // Créez un document vierge.
    Document dstDoc = new Document();
    // Supprimez le premier paragraphe du document vide.
    dstDoc.FirstSection.Body.RemoveAllChildren();

    // Importez chaque nœud de la liste dans le nouveau document. Conservez la mise en forme d'origine du nœud.
    NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KeepSourceFormatting);

    foreach (Node node in nodes)
    {
        Node importNode = importer.ImportNode(node, true);
        dstDoc.FirstSection.Body.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.

Extraire du texte entre les paragraphes d’un document Word

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

  • 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.FirstSection.Body.GetChild(NodeType.PARAGRAPH, int, boolean).
  • 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 document Word en C#.

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

// Rassemblez les nœuds (la méthode GetChild utilise un index basé sur 0)
Paragraph startPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 6, true);
Paragraph endPara = (Paragraph)doc.FirstSection.Body.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 et enregistrez-le sur le disque.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Extraire du texte entre différents types de nœuds dans un document Word

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 effectuer cette opération.

  • 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.FirstSection.Body.GetChild(NodeType, int, boolean).
  • 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 en C#.

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

Paragraph startPara = (Paragraph)doc.LastSection.GetChild(NodeType.Paragraph, 2, true);
Table endTable = (Table)doc.LastSection.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);

// Insérez le contenu dans un nouveau document et enregistrez-le sur le disque.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Extraire le texte 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 C#.

  • 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 ParagraphsByStyleName(Document, “Heading 1”).
  • Extrayez des paragraphes dans un autre objet à l’aide de la méthode d’assistance ParagraphsByStyleName(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 Word
Document doc = new Document("document.docx");

// Rassemblez une liste des paragraphes en utilisant les styles de titre respectifs.
List<Paragraph> parasStyleHeading1 = ParagraphsByStyleName(doc, "Heading 1");
List<Paragraph> parasStyleHeading3 = ParagraphsByStyleName(doc, "Heading 3");

// Utilisez la première instance des paragraphes avec ces styles.
Node startPara1 = (Node)parasStyleHeading1[0];
Node endPara1 = (Node)parasStyleHeading3[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 et enregistrez-le sur le disque.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Lire la suite

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

Obtenez une licence API gratuite

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

Conclusion

Dans cet article, vous avez appris à extraire du texte de documents MS Word à l’aide de C#. De plus, vous avez vu comment extraire par programme 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 C#. En outre, vous pouvez explorer d’autres fonctionnalités d’Aspose.Words for .NET à l’aide de la documentation. Si vous avez des questions, n’hésitez pas à nous en faire part via notre forum.

Voir également

Conseil : vous pouvez vérifier le convertisseur Aspose PowerPoint to Word, car il illustre le processus populaire de conversion de présentation en document Word.