Extrair texto de documentos do MS Word em C#

A extração de texto de documentos do Word geralmente é realizada em diferentes cenários. Por exemplo, para analisar o texto, extrair seções específicas de um documento e combiná-las em um único documento e assim por diante. Neste artigo, você aprenderá como extrair texto de documentos do Word programaticamente em C#. Além disso, abordaremos como extrair conteúdo entre elementos específicos, como parágrafos, tabelas, etc. dinamicamente.

Biblioteca C# para extrair texto de documentos do Word

Aspose.Words for .NET é uma biblioteca poderosa que permite que você crie documentos do MS Word a partir do zero. Além disso, permite que você manipule os documentos existentes do Word para criptografia, conversão, extração de texto, etc. Usaremos esta biblioteca para extrair texto dos documentos Word DOCX ou DOC. Você pode baixar a DLL da API ou instalá-la diretamente do NuGet usando o console do gerenciador de pacotes.

PM> Install-Package Aspose.Words

Extração de texto em documentos do Word usando C#

Um documento MS Word consiste em vários elementos que incluem parágrafos, tabelas, imagens, etc. Portanto, os requisitos de extração de texto podem variar de um cenário para outro. Por exemplo, pode ser necessário extrair texto entre parágrafos, marcadores, comentários, etc.

Cada tipo de elemento em um documento do Word é representado como um nó. Portanto, para processar um documento, você terá que brincar com os nós. Então vamos começar e ver como extrair texto de documentos do Word em diferentes cenários.

Extrair texto de um documento do Word em C#

Nesta seção, vamos implementar um extrator de texto C# para documentos do Word e o fluxo de trabalho de extração de texto seria o seguinte:

  • Primeiro, vamos definir os nós que queremos incluir no processo de extração de texto.
  • Em seguida, extrairemos o conteúdo entre os nós especificados (incluindo ou excluindo os nós inicial e final).
  • Finalmente, usaremos o clone de nós extraídos, por exemplo, para criar um novo documento do Word consistindo de conteúdo extraído.

Vamos agora escrever um método chamado ExtractContent para o qual passaremos os nós e alguns outros parâmetros para realizar a extração do texto. Esse método analisará o documento e clonará os nós. A seguir estão os parâmetros que passaremos para este método.

  1. StartNode e EndNode como pontos inicial e final para a extração do conteúdo, respectivamente. Estes podem ser nós de nível de bloco (Parágrafo, Tabela) ou de nível embutido (por exemplo, Run, FieldStart, BookmarkStart etc.).
    1. Para passar um campo você deve passar o objeto FieldStart correspondente.
    2. Para passar marcadores, os nós BookmarkStart e BookmarkEnd devem ser passados.
    3. Para comentários, os nós CommentRangeStart e CommentRangeEnd devem ser usados.
  2. IsInclusive define se os marcadores estão incluídos na extração ou não. Se esta opção for definida como false e o mesmo nó ou nós consecutivos forem passados, uma lista vazia será retornada.

A seguir está a implementação completa do método ExtractContent que extrai o conteúdo entre os nós que são passados.

public static ArrayList ExtractContent(Node startNode, Node endNode, bool isInclusive)
{
    // Primeiro verifique se os nós passados para este método são válidos para uso.
    VerifyParameterNodes(startNode, endNode);

    // Crie uma lista para armazenar os nós extraídos.
    ArrayList nodes = new ArrayList();

    // Mantenha um registro dos nós originais passados para este método para que possamos dividir os nós marcadores, se necessário.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Extraia conteúdo com base em nós de nível de bloco (parágrafos e tabelas). Percorra os nós pais para encontrá-los.
    // Vamos dividir o conteúdo do primeiro e do último nós dependendo se os nós marcadores estão embutidos
    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;
    // O nó atual que estamos extraindo do documento.
    Node currNode = startNode;

    // Comece a extrair o conteúdo. Processe todos os nós de nível de bloco e divida especificamente o primeiro e o último nós quando necessário, para que a formatação do parágrafo seja mantida.
    // O método é um pouco mais complexo do que um extrator regular, pois precisamos fatorar na extração usando nós, campos, marcadores etc. inline para torná-lo realmente útil.
    while (isExtracting)
    {
        // Clone o nó atual e seus filhos para obter uma cópia.
        Node cloneNode = currNode.Clone(true);
        isEndingNode = currNode.Equals(endNode);

        if ((isStartingNode || isEndingNode) && cloneNode.IsComposite)
        {
            // Precisamos processar cada marcador separadamente, então passe-o para um método separado.
            if (isStartingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // O condicional precisa ser separado, pois os marcadores de início e fim de nível de bloco podem ser o mesmo nó.
            if (isEndingNode)
            {
                ProcessMarker((CompositeNode)cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        }
        else
            // O nó não é um marcador inicial ou final, basta adicionar a cópia à lista.
            nodes.Add(cloneNode);

        // Mova para o próximo nó e extraia-o. Se o próximo nó for nulo, isso significa que o restante do conteúdo foi encontrado em uma seção diferente.
        if (currNode.NextSibling == null && isExtracting)
        {
            // Mover para a próxima seção.
            Section nextSection = (Section)currNode.GetAncestor(NodeType.Section).NextSibling;
            currNode = nextSection.Body.FirstChild;
        }
        else
        {
            // Mover para o próximo nó no corpo.
            currNode = currNode.NextSibling;
        }
    }

    // Retorne os nós entre os marcadores de nós.
    return nodes;
}

Alguns métodos auxiliares também são exigidos pelo método ExtractContent para realizar a operação de extração de texto, que são fornecidas abaixo.

public static List<Paragraph> ParagraphsByStyleName(Document doc, string styleName)
{
    // Crie uma matriz para coletar parágrafos do estilo especificado.
    List<Paragraph> paragraphsWithStyle = new List<Paragraph>();

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

    // Examine todos os parágrafos para encontrar aqueles com o estilo especificado.
    foreach (Paragraph paragraph in paragraphs)
    {
        if (paragraph.ParagraphFormat.Style.Name == styleName)
            paragraphsWithStyle.Add(paragraph);
    }

    return paragraphsWithStyle;
}
private static void VerifyParameterNodes(Node startNode, Node endNode)
{
    // A ordem em que essas verificações são feitas é importante.
    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");

    // Verifique se o nó final está após o nó inicial na árvore DOM
    // Primeiro verifique se eles estão em seções diferentes, depois, se não estiverem, verifique sua posição no corpo da mesma seção em que estão.
    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)
{
    // Testar se o nó é descendente de um nó Parágrafo ou Tabela e também não é um parágrafo ou uma tabela um parágrafo dentro de uma classe de comentário que é descendente de um parágrafo é possível.
    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)
{
    // Se estivermos lidando com um nó em nível de bloco, basta ver se ele deve ser incluído e adicioná-lo à lista.
    if (!IsInline(node))
    {
        // Não adicione o nó duas vezes se os marcadores forem o mesmo nó
        if (!(isStartMarker && isEndMarker))
        {
            if (isInclusive)
                nodes.Add(cloneNode);
        }
        return;
    }

    // Se um marcador é um nó FieldStart, verifique se ele deve ser incluído ou não.
    // Assumimos por simplicidade que FieldStart e FieldEnd aparecem no mesmo parágrafo.
    if (node.NodeType == NodeType.FieldStart)
    {
        // Se o marcador for um nó inicial e não for incluído, pule para o final do campo.
        // Se o marcador for um nó final e deve ser incluído, mova para o campo final para que o campo não seja removido.
        if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive))
        {
            while (node.NextSibling != null && node.NodeType != NodeType.FieldEnd)
                node = node.NextSibling;

        }
    }

    // Se qualquer um dos marcadores fizer parte de um comentário, para incluir o comentário em si, precisamos mover o ponteiro para o Comentário
    // Nó encontrado após o nó CommentRangeEnd.
    if (node.NodeType == NodeType.CommentRangeEnd)
    {
        while (node.NextSibling != null && node.NodeType != NodeType.Comment)
            node = node.NextSibling;

    }

    // Encontre o nó correspondente em nosso nó clonado por índice e retorne-o.
    // Se o nó inicial e final forem os mesmos, alguns nós filhos já podem ter sido removidos. Subtraia o
    // Diferença para obter o índice correto.
    int indexDiff = node.ParentNode.ChildNodes.Count - cloneNode.ChildNodes.Count;

    // Contagem de nós filho idêntica.
    if (indexDiff == 0)
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node)];
    else
        node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node) - indexDiff];

    // Remova os nós até/do marcador.
    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();
    }

    // Após o processamento, o nó composto pode ficar vazio. Se tiver não inclua.
    if (!(isStartMarker && isEndMarker))
    {
        if (cloneNode.HasChildNodes)
            nodes.Add(cloneNode);
    }

}
public static Document GenerateDocument(Document srcDoc, ArrayList nodes)
{
    // Crie um documento em branco.
    Document dstDoc = new Document();
    // Remova o primeiro parágrafo do documento vazio.
    dstDoc.FirstSection.Body.RemoveAllChildren();

    // Importe cada nó da lista para o novo documento. Mantenha a formatação original do nó.
    NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KeepSourceFormatting);

    foreach (Node node in nodes)
    {
        Node importNode = importer.ImportNode(node, true);
        dstDoc.FirstSection.Body.AppendChild(importNode);
    }

    // Devolva o documento gerado.
    return dstDoc;
}

Agora estamos prontos para utilizar esses métodos e extrair texto de um documento do Word.

Extrair texto entre parágrafos em um documento do Word

Vamos ver como extrair conteúdo entre dois parágrafos em um documento Word DOCX. A seguir estão as etapas para executar esta operação em C#.

  • Primeiro, carregue o documento do Word usando a classe Document.
  • Obtenha a referência dos parágrafos inicial e final em dois objetos usando o método Document.FirstSection.Body.GetChild(NodeType.PARAGRAPH, int, boolean).
  • Chame o método ExtractContent(startPara, endPara, True) para extrair os nós em um objeto.
  • Chame o método auxiliar GenerateDocument(Document, ExtractNodes) para criar um documento que consiste no conteúdo extraído.
  • Finalmente, salve o documento retornado usando o método Document.Save(string).

O exemplo de código a seguir mostra como extrair texto entre os parágrafos 7 e 11 em um documento do Word em C#.

// Carregar documento do Word
Document doc = new Document("document.docx");

// Reúna os nós (o método GetChild usa o índice baseado em 0)
Paragraph startPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 6, true);
Paragraph endPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 10, true);

// Extraia o conteúdo entre esses nós no documento. Inclua esses marcadores na extração.
ArrayList extractedNodes = ExtractContent(startPara, endPara, true);

// Insira o conteúdo em um novo documento e salve-o em disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Extrair texto entre diferentes tipos de nós em um documento do Word

Você também pode extrair conteúdo entre diferentes tipos de nós. Para demonstração, vamos extrair o conteúdo entre um parágrafo e uma tabela e salvá-lo em um novo documento do Word. A seguir estão as etapas para realizar esta operação.

  • Carregue o documento do Word usando a classe Document.
  • Obtenha a referência dos nós inicial e final em dois objetos usando o método Document.FirstSection.Body.GetChild(NodeType, int, boolean).
  • Chame o método ExtractContent(startPara, endPara, True) para extrair os nós em um objeto.
  • Chame o método auxiliar GenerateDocument(Document, ExtractNodes) para criar um documento que consiste no conteúdo extraído.
  • Salve o documento retornado usando o método Document.Save(string).

O exemplo de código a seguir mostra como extrair texto entre um parágrafo e uma tabela em C#.

// Carregar documento do 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);

// Extraia o conteúdo entre esses nós no documento. Inclua esses marcadores na extração.
ArrayList extractedNodes = ExtractContent(startPara, endTable, true);

// Insira o conteúdo em um novo documento e salve-o em disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

Extrair texto entre parágrafos com base em estilos

Vamos agora verificar como extrair conteúdo entre parágrafos com base em estilos. Para demonstração, vamos extrair o conteúdo entre o primeiro “Título 1” e o primeiro “Título 3” no documento do Word. As etapas a seguir demonstram como fazer isso em C#.

  • Primeiro, carregue o documento do Word usando a classe Document.
  • Em seguida, extraia parágrafos em um objeto usando o método auxiliar ParagraphsByStyleName(Document, “Heading 1”).
  • Extraia parágrafos em outro objeto usando o método auxiliar ParagraphsByStyleName(Document, “Heading 3”).
  • Chame o método ExtractContent(startPara, endPara, True) e passe os primeiros elementos em ambas as matrizes de parágrafo como primeiro e segundo parâmetros.
  • Chame o método auxiliar GenerateDocument(Document, ExtractNodes) para criar um documento que consiste no conteúdo extraído.
  • Finalmente, salve o documento retornado usando o método Document.Save(string).

O exemplo de código a seguir mostra como extrair conteúdo entre parágrafos com base em estilos.

// Carregar documento do Word
Document doc = new Document("document.docx");

// Reúna uma lista dos parágrafos usando os respectivos estilos de título.
List<Paragraph> parasStyleHeading1 = ParagraphsByStyleName(doc, "Heading 1");
List<Paragraph> parasStyleHeading3 = ParagraphsByStyleName(doc, "Heading 3");

// Use a primeira instância dos parágrafos com esses estilos.
Node startPara1 = (Node)parasStyleHeading1[0];
Node endPara1 = (Node)parasStyleHeading3[0];

// Extraia o conteúdo entre esses nós no documento. Não inclua esses marcadores na extração.
ArrayList extractedNodes = ExtractContent(startPara1, endPara1, false);

// Insira o conteúdo em um novo documento e salve-o em disco.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");

consulte Mais informação

Você pode explorar outros cenários de extração de texto de documentos do Word usando este artigo de documentação.

Obtenha uma licença de API gratuita

Você pode obter uma licença temporária para usar o Aspose.Words para .NET sem limitações de avaliação.

Conclusão

Neste artigo, você aprendeu como extrair texto de documentos do MS Word usando C#. Além disso, você viu como extrair conteúdo entre tipos de nós semelhantes ou diferentes em um documento do Word programaticamente. Assim, você pode construir seu próprio extrator de texto do MS Word em C#. Além disso, você pode explorar outros recursos do Aspose.Words para .NET usando a documentação. Caso você tenha alguma dúvida, sinta-se à vontade para nos informar através do nosso fórum.

Veja também

Dica: Você pode querer verificar Aspose PowerPoint to Word Converter porque demonstra o processo de conversão de apresentação para documento do Word popular.