2016-08-24 1 views
3

상황VBA에서의 XML을 통해 Word 문서의 내용을 읽을 방법

내가 (약 1000) 문서의 많은 양의 물마루 선별하기 위해 Excel에서 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을 사용하여 문서 이름을 바꿀 때 액세스 할 수 있습니다.

텍스트 검색에서 사용하지 않는 단어 문서의 모든 이미지, 차트 및 표를로드하는 것보다 훨씬 빠릅니다.

따라서, 나는 zip 파일 및 XML 문서가 다음 VBA에서 문자의 일반 문자열처럼 처리에 대한 액세스와 같은 워드 문서를 열 VBA에서 수있는 방법이 있다면 내가 이미 가지고 있기 때문에, 물어보고 싶은게 위의 코드가 주어진 파일의 경로와 이름.

+1

셸 개체 (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)이 있습니다).

좋아, 나는 대답을 가능한 한 설명하기 위해 노력할 것이다.

우선이 질문을 통해 C# 및 XPath의 무한한 여정으로 연결되어 어떤 점을 추구하지 않기로 결정했습니다.

파일 분석 시간이 약 2 시간에서 10 초로 단축되었습니다.

컨텍스트

는 XML 문서를 읽기의 백본, 따라서 내부 워드의 XML 문서, Microsoft의 OpenXML을 라이브러리입니다. 위에서 말한 것을 명심하십시오. 구현하려는 방법은 VBA에서만 수행 할 수 없으므로 다른 방법으로 수행해야합니다. 이것은 VBA가 Office에 구현되어있어 Office 문서의 핵심 구조에 액세스하는 데 제한이 있기 때문에 가능하지만이 제한 사항과 관련된 정보는 없습니다 (모든 정보는 환영합니다).

여기서는 VBA 용 C# DLL을 작성합니다. C#에서 DLL을 작성하고 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) 이 문서에서는 Word 문서 조작을 위해 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; 
      } 
     } 
    } 

} 

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 스캔 할 수있는 도구를 얻을 관련된 VBA 코드 더 많은 코드 작성 비용으로 VBA에서 읽기/닫기 접근 방식을 사용합니다.

특히 간단한 단어 검색을 수행하려는 사용자에게 간단한 솔루션이 제공됩니다 (특히 엄청난 수의 단어 문서가있는 경우). @Mikegrann에 의해 지적

구문 분석 말씀 .XML 파일은 VBA에 악몽이 될 수 있습니다. 고맙게도 OpenXML에는 C#에서 사용자를 위해 XML 구문 분석기 C# , xml parsing. get data between tags이 있으며 문서의 텍스트를 참조하는 <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'라고 언급 한 것을 보았습니다. 사과, –