2016-08-24 1 views
3

КонтекстКак читать содержимое документа Word, с помощью его XML в VBA

Я пытаюсь создать документ Word, браузер в Excel, чтобы просеять корыто большого количества документов (около 1000).

Процесс открытия документа Word оказывается довольно медленным (около 4 секунд на каждый документ, поэтому в этом случае требуется 2 часа, чтобы просмотреть все элементы, что слишком медленно для одного запроса), даже отключив все вещи, которые могли бы замедлить открытие, поэтому я открываю:

  • Как только читать
  • Без открытого и ремонта режима (что может произойти в некоторых документах)
  • отключение отображения документа

Моя попытка до сих пор

Эти документы сложно просматривать, потому что некоторые ключевые слова появляются каждый раз, но не в том же контексте (а не основной проблемы здесь, так как я могу справиться с этим, когда текст загружается в массивы). Следовательно, часто используемое решение Windows explorer (как в этом link) не может быть использовано в моем случае.

На данный момент мне удалось создать рабочий макрос, который анализирует содержимое документов слов, открывая их.

Код

Вот пример кода. Обратите внимание, что я использовал Microsoft Word 14.0 Object Library ссылки

' Analyzing all the word document within the same folder ' 
Sub extractFile() 

Dim i As Long, j As Long 
Dim sAnalyzedDoc As String, sLibName As String 
Dim aOut() 
Dim oWordApp As Word.Application 
Dim oDoc As Word.Document 

Set oWordApp = CreateObject("Word.Application") 

sLibName = ThisWorkbook.Path & "\" 
sAnalyzedDoc = Dir(sLibName) 
sKeyword = "example of a word" 

With Application 
    .DisplayAlerts = False 
    .ScreenUpdating = False 
End With 

ReDim aOut(2, 2) 
aOut(1, 1) = "Document name" 
aOut(2, 1) = "Text" 


While (sAnalyzedDoc <> "") 
    ' Analyzing documents only with the .doc and .docx extension ' 
    If Not InStr(sAnalyzedDoc, ".doc") = 0 Then 
     ' Opening the document as mentionned above, in read only mode, without repair and invisible ' 
     Set oDoc = Word.Documents.Open(sLibName & "\" & sAnalyzedDoc, ReadOnly:=True, OpenAndRepair:=False, Visible:=False) 
     With oDoc 
      For i = 1 To .Sentences.Count 
       ' Searching for the keyword within the document ' 
       If Not InStr(LCase(.Sentences.Item(i)), LCase(sKeyword)) = 0 Then 
        If Not IsEmpty(aOut(1, 2)) Then 
         ReDim Preserve aOut(2, UBound(aOut, 2) + 1) 
        End If 
        aOut(1, UBound(aOut, 2)) = sAnalyzedDoc 
        aOut(2, UBound(aOut, 2)) = .Sentences.Item(i) 
        GoTo closingDoc ' A dubious programming choice but that works for the moment ' 
       End If 
      Next i 
closingDoc: 
      ' Intending to make the closing faster by not saving the document ' 
      .Close SaveChanges:=False 
     End With 
    End If 
    'Moving on to the next document ' 
    sAnalyzedDoc = Dir 
Wend 

exitSub: 
With Output 
    .Range(.Cells(1, 1), .Cells(UBound(aOut, 1), UBound(aOut, 2))) = aOut 
End With 

With Application 
    .DisplayAlerts = True 
    .ScreenUpdating = True 
End With 

End Sub 

Мой вопрос

Идею я думал пойти черезXML содержания в документе, прямой доступ к его содержанию (которое вам может получить доступ при переименовании любого документа в новых версиях Word с расширением .zip и для nameOfDocument.zip\word\document.xml).

Это было бы намного быстрее, чем загрузка всех изображений, диаграмм и таблиц слова документа, которые бесполезны в текстовом поиске.

Таким образом, я хотел бы спросить, есть ли способ в VBA, чтобы открыть текстовый документ, как почтовый файл и доступ, XML документ затем обработать его как обычную строку символов в VBA, так как у меня уже есть путь и имя файла с учетом вышеуказанного кода.

+1

Вы можете получить доступ к файлам с zip-файлами напрямую через объект Shell (http://www.rondebruin.nl/win/s7/win002.htm), но тогда вы будете зацикливаться на разборе XML (http: // stackoverflow .com/questions/11305/how-to-parse-xml-using-vba), и Word имеет ужасный базовый xml для работы. Удачи. – Mikegrann

+1

Взгляните на [макрос VBA, чтобы найти папку для ключевого слова] (http://stackoverflow.com/a/20856265/4717755). Используя функцию «FindFiles», как описано (используйте вторую версию), вы будете использовать индекс Windows всех слов в ваших документах. – PeterT

+0

Спасибо вам обоим, я посмотрю ссылки и попытаюсь что-то сделать. –

ответ

2

Обратите внимание, что это нелегкий ответ на вышеуказанную проблему, и единственный код VBA в моем первоначальном вопросе будет отлично выполнять эту работу, если у вас нет загрузки документов для просмотра, иначе перейдите к другому инструмент (есть Python Dynamic Link Library (DLL), который делает это очень хорошо).

Хорошо, я постараюсь сделать свой ответ максимально объяснительным.

Прежде всего этот вопрос приведет меня к бесконечному путешествию XML в C# и в XPath, который я решил не преследовать в какой-то момент.

Это сократило время анализа файлов примерно с 2 часов до 10 секунд.

Контекст

Костяк чтения XML-документов, и поэтому внутренние документы XML слово, является OpenXML библиотеки от Microsoft. Имейте в виду, что я сказал выше, что метод, который я пытался реализовать, не может быть выполнен исключительно в VBA и, следовательно, должен быть выполнен по-другому. Возможно, это связано с тем, что VBA реализован для Office и, таким образом, ограничен в доступе к основной структуре документов Office, но у меня нет информации об этом ограничении (любая информация приветствуется).

Ответ, который я приведу здесь, представляет собой C# DLL для VBA. Для написания DLL в C# и ссылки на него в VBA перенаправить вас к следующей ссылке, которая будет возобновлена ​​в лучшую сторону этот конкретный процесс: Tutorial for creating dll in C#

Давайте начнем

Прежде всего, вам нужно будет обратитесь к библиотеке WindowsBase и DocumentFormat.OpenXML в свой проект, чтобы сделать работу с решением, как описано в этой статье MSDN Manipulate Office Open XML Formats Documents, и что один Open and add text to a word processing document (Open XML SDK) В этих статьях подробно объясняются, как работает библиотека OpenXML для обработки текстовых документов.

C# код

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Xml; 
using System.IO.Packaging; 

namespace BrowserClass 
{ 

    public class SpecificDirectory 
    { 

     public string[,] LookUpWord(string nameKeyword, string nameStopword, string nameDirectory) 
     { 
      string sKeyWord = nameKeyword; 
      string sStopWord = nameStopword; 
      string sDirectory = nameDirectory; 

      sStopWord = sStopWord.ToLower(); 
      sKeyWord = sKeyWord.ToLower(); 

      string sDocPath = Path.GetDirectoryName(sDirectory); 
      // Looking for all the documents with the .docx extension 
      string[] sDocName = Directory.GetFiles(sDocPath, "*.docx", SearchOption.AllDirectories); 
      string[] sDocumentList = new string[1]; 
      string[] sDocumentText = new string[1]; 

      // Cycling the documents retrieved in the folder 
      for (int i = 0; i < sDocName.Count(); i++) 
      { 
       string docWord = sDocName[i]; 

       // Opening the documents as read only, no need to edit them 
       Package officePackage = Package.Open(docWord, FileMode.Open, FileAccess.Read); 

       const String officeDocRelType = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; 

       PackagePart corePart = null; 
       Uri documentUri = null; 

       // We are extracting the part with the document content within the files 
       foreach (PackageRelationship relationship in officePackage.GetRelationshipsByType(officeDocRelType)) 
       { 
        documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri); 
        corePart = officePackage.GetPart(documentUri); 
        break; 
       } 

       // Here enter the proper code 
       if (corePart != null) 
       { 
        string cpPropertiesSchema = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"; 
        string dcPropertiesSchema = "http://purl.org/dc/elements/1.1/"; 
        string dcTermsPropertiesSchema = "http://purl.org/dc/terms/"; 

        // Construction of a namespace manager to handle the different parts of the xml files 
        NameTable nt = new NameTable(); 
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt); 
        nsmgr.AddNamespace("dc", dcPropertiesSchema); 
        nsmgr.AddNamespace("cp", cpPropertiesSchema); 
        nsmgr.AddNamespace("dcterms", dcTermsPropertiesSchema); 

        // Loading the xml document's text 
        XmlDocument doc = new XmlDocument(nt); 
        doc.Load(corePart.GetStream()); 

        // I chose to directly load the inner text because I could not parse the way I wanted the document, but it works so far 
        string docInnerText = doc.DocumentElement.InnerText; 
        docInnerText = docInnerText.Replace("\\* MERGEFORMAT", "."); 
        docInnerText = docInnerText.Replace("DOCPROPERTY ", ""); 
        docInnerText = docInnerText.Replace("Glossary.", ""); 

        try 
        { 
         Int32 iPosKeyword = docInnerText.ToLower().IndexOf(sKeyWord); 
         Int32 iPosStopWord = docInnerText.ToLower().IndexOf(sStopWord); 

         if (iPosStopWord == -1) 
         { 
          iPosStopWord = docInnerText.Length; 
         } 

         if (iPosKeyword != -1 && iPosKeyword <= iPosStopWord) 
         { 
          // Redimensions the array if there was already a document loaded 
          if (sDocumentList[0] != null) 
          { 
           Array.Resize(ref sDocumentList, sDocumentList.Length + 1); 
           Array.Resize(ref sDocumentText, sDocumentText.Length + 1); 
          } 
          sDocumentList[sDocumentList.Length - 1] = docWord.Substring(sDocPath.Length, docWord.Length - sDocPath.Length); 
          // Taking the small context around the keyword 
          sDocumentText[sDocumentText.Length - 1] = ("(...) " + docInnerText.Substring(iPosKeyword, sKeyWord.Length + 60) + " (...)"); 
         } 

        } 
        catch (ArgumentOutOfRangeException) 
        { 
         Console.WriteLine("Error reading inner text."); 
        } 
       } 
       // Closing the package to enable opening a document right after 
       officePackage.Close(); 
      } 

      if (sDocumentList[0] != null) 
      { 
       // Preparing the array for output 
       string[,] sFinalArray = new string[sDocumentList.Length, 2]; 

       for (int i = 0; i < sDocumentList.Length; i++) 
       { 
        sFinalArray[i, 0] = sDocumentList[i].Replace("\\", ""); 
        sFinalArray[i, 1] = sDocumentText[i]; 
       } 
       return sFinalArray; 
      } 
      else 
      { 
       // Preparing the array for output 
       string[,] sFinalArray = new string[1, 1]; 
       sFinalArray[0, 0] = "NO MATCH"; 
       return sFinalArray; 
      } 
     } 
    } 

} 

Код VBA связанный

Option Explicit 

Const sLibname As String = "C:\pathToYourDocuments\" 

Sub tester() 

Dim aFiles As Variant 
Dim LookUpDir As BrowserClass.SpecificDirectory 
Set LookUpDir = New BrowserClass.SpecificDirectory 

' The array will contain all the files which contain the "searchedPhrase" ' 
aFiles = LookUpDir.LookUpWord("searchedPhrase", "stopWord", sLibname) 

' Add here any necessary processing if needed ' 

End Sub 

Таким образом, в конце концов, вы получаете инструмент, который может сканировать .docx документы гораздо быстрее, чем в классическом открытой -read-close в VBA за счет большего количества написания кода.

Прежде всего, вы получаете простое решение для своих пользователей, которое просто хочет выполнить простой поиск, особенно когда имеется огромное количество текстовых документов.

Примечания

Синтаксического Слово .XML файлы могут быть кошмарными в VBA как отметили @Mikegrann. К счастью, OpenXML имеет синтаксический анализатор XML C# , xml parsing. get data between tags, который выполнит работу для вас на C# и возьмет теги <w:t></w:t>, которые ссылаются на текст документа. Хотя я нашел ответы на эти вопросы до сих пор, но не мог заставить их работать: Parsing a MS Word generated XML file in C#, Reading specific XML elements from XML file

Так что я пошел на .InnerText решения, которое я снабженным мой код выше, чтобы получить доступ к внутреннему текста, за счет наличия форматирования текстовый ввод (например, \\MERGEFORMAT).

+0

Да, я забыл добавить это. Теперь запрос занимает приблизительно 10 секунд для выполнения для всех файлов, но он ограничивается форматированием файлов .doc –

+0

Отлично! Подумайте о публикации вопроса C# и попросите гуру показать, как вы можете интегрировать * .docx * файлы. – Parfait

+0

Черт возьми, я только что вернулся, чтобы увидеть, что я упомянул '.doc', когда на самом деле этот ответ применяется только к файлам .docx, потому что файлы' .doc' имеют другой способ хранения данных. Извинения, –

Смежные вопросы