2009-12-21 32 views
4

.Docx文檔似乎沒有被編入索引。用於docx分析器錯誤的word ifilter

我在.docx中使用了一個唯一的字符串,但是當我搜索「one」時,不會返回.docx。

例如,以下是以下文字:「這裏是一個行的文本,這裏是兩行文本」

將通過IFilter的提取爲:

「這裏是行oneand這裏的文字是兩行文字。」

因此,當Ifilter解析.docx時,他刪除換行符分隔符並嘗試解析「oneand here」...。

因此,看起來像.docx的ifilter將一行的最後一個字與下一行的第一個字連接起來。

任何人都可以提供一些如何解決這個問題的想法嗎?

在此先感謝。

+0

屬於超級用戶 – Oded

+0

看過ifilter後......似乎現在的實現有問題......我自己解析docx xml以避免出現這樣的問題 – bastianneu

+0

Thanks Bastianneu。 是否可以擴展IFilter來糾正這個問題? 由於IFilter工作在PDF和舊版本的doc上,所以使用不同的只適用於這種新類型並不是一個好策略。 – Txugo

回答

2

好吧我現在想出了這一個。基本上64位IFilter工作不正常。它合併由換行符分隔的單詞,但不會將它們帶入。我使用Ionic.zip訪問docx zip存檔並使用DocxToText的稍微修改版本分析重要的xml文件。這現在完美。

這裏最初是由Jevgenij潘科夫創建

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Ionic.Zip; 
using System.IO; 
using System.Xml; 

public class DocxToText 
{ 
    private const string ContentTypeNamespace = 
     @"http://schemas.openxmlformats.org/package/2006/content-types"; 

    private const string WordprocessingMlNamespace = 
     @"http://schemas.openxmlformats.org/wordprocessingml/2006/main"; 

    private const string DocumentXmlXPath = 
     "/t:Types/t:Override[@ContentType=\"" + 
     "application/vnd.openxmlformats-officedocument." + 
     "wordprocessingml.document.main+xml\"]"; 

    private const string BodyXPath = "/w:document/w:body"; 

    private string docxFile = ""; 
    private string docxFileLocation = ""; 

    public DocxToText(string fileName) 
    { 
     docxFile = fileName; 
    } 

    #region ExtractText() 
    /// 

    /// Extracts text from the Docx file. 

    /// 

    /// Extracted text. 

    public string ExtractText() 
    { 
     if (string.IsNullOrEmpty(docxFile)) 
      throw new Exception("Input file not specified."); 

     // Usually it is "/word/document.xml" 


     docxFileLocation = FindDocumentXmlLocation(); 

     if (string.IsNullOrEmpty(docxFileLocation)) 
      throw new Exception("It is not a valid Docx file."); 

     return ReadDocumentXml(); 
    } 
    #endregion 

    #region FindDocumentXmlLocation() 
    /// 

    /// Gets location of the "document.xml" zip entry. 

    /// 

    /// Location of the "document.xml". 

    private string FindDocumentXmlLocation() 
    { 
     using (ZipFile zip = new ZipFile(docxFile)) 
     { 
      foreach (ZipEntry entry in zip) 
      { 
       // Find "[Content_Types].xml" zip entry 
       if (string.Compare(entry.FileName, "[Content_Types].xml", true) == 0) 
       { 
        XmlDocument xmlDoc = new XmlDocument(); 
        using (var stream = new MemoryStream()) 
        { 

         entry.Extract(stream); 
         stream.Position = 0; 

         xmlDoc.PreserveWhitespace = true; 
         xmlDoc.Load(stream); 
        } 

        //Create an XmlNamespaceManager for resolving namespaces 


        XmlNamespaceManager nsmgr = 
         new XmlNamespaceManager(xmlDoc.NameTable); 
        nsmgr.AddNamespace("t", ContentTypeNamespace); 

        // Find location of "document.xml" 


        XmlNode node = xmlDoc.DocumentElement.SelectSingleNode(
         DocumentXmlXPath, nsmgr); 

        if (node != null) 
        { 
         string location = 
          ((XmlElement)node).GetAttribute("PartName"); 
         return location.TrimStart(new char[] { '/' }); 
        } 
        break; 
       } 
      } 
     } 
     return null; 
    } 
    #endregion 

    #region ReadDocumentXml() 
    /// 

    /// Reads "document.xml" zip entry. 

    /// 

    /// Text containing in the document. 

    private string ReadDocumentXml() 
    { 
     StringBuilder sb = new StringBuilder(); 

     using (ZipFile zip = new ZipFile(docxFile)) 
     { 
      foreach (ZipEntry entry in zip) 
      { 
       if (string.Compare(entry.FileName, docxFileLocation, true) == 0) 
       { 
        XmlDocument xmlDoc = new XmlDocument(); 
        using (var stream = new MemoryStream()) 
        { 

         entry.Extract(stream); 
         stream.Position = 0; 

         xmlDoc.PreserveWhitespace = true; 
         xmlDoc.Load(stream); 
        } 

        XmlNamespaceManager nsmgr = 
         new XmlNamespaceManager(xmlDoc.NameTable); 
        nsmgr.AddNamespace("w", WordprocessingMlNamespace); 

        XmlNode node = 
         xmlDoc.DocumentElement.SelectSingleNode(BodyXPath, nsmgr); 

        if (node == null) 
         return string.Empty; 

        sb.Append(ReadNode(node)); 

        break; 
       } 
      } 
     } 
     return sb.ToString(); 
    } 
    #endregion 

    #region ReadNode() 
    /// 

    /// Reads content of the node and its nested childs. 

    /// 

    /// XmlNode. 

    /// Text containing in the node. 

    private string ReadNode(XmlNode node) 
    { 
     if (node == null || node.NodeType != XmlNodeType.Element) 
      return string.Empty; 

     StringBuilder sb = new StringBuilder(); 
     foreach (XmlNode child in node.ChildNodes) 
     { 
      if (child.NodeType != XmlNodeType.Element) continue; 

      switch (child.LocalName) 
      { 
       case "t": // Text 

        sb.Append(child.InnerText.TrimEnd()); 

        string space = 
         ((XmlElement)child).GetAttribute("xml:space"); 
        if (!string.IsNullOrEmpty(space) && 
         space == "preserve") 
         sb.Append(' '); 

        break; 

       case "cr":       // Carriage return 

       case "br":       // Page break 

        sb.Append(Environment.NewLine); 
        break; 

       case "tab":       // Tab 

        sb.Append("\t"); 
        break; 

       case "p":       // Paragraph 

        sb.Append(ReadNode(child)); 
        sb.Append(Environment.NewLine); 
        sb.Append(Environment.NewLine); 
        break; 

       default: 
        sb.Append(ReadNode(child)); 
        break; 
      } 
     } 
     return sb.ToString(); 
    } 
    #endregion 
} 

下面是該代碼使用修改後的代碼...

DocxToText dtt = new DocxToText(filepath); 
string docxText = dtt.ExtractText(); 
+0

感謝分享。很有用。 –

1

把光標放在一個單詞中間和保存文檔將導致該單詞在兩個XML標籤中分開,並在兩者之間有一個「_GoBack」書籤。結果是在用這個例程解析之後,在這兩個字符串片段之間放置一個空格,而不是將它們合併回一個字符串。處理「_GoBack」場景很容易,但也可能有其他場景。也許「追蹤變化」,誰知道還有什麼。

DOCX有更詳細的解析算法嗎?