如何在C#中从MS Office文档中提取文本

42

我想使用C#从MS Word(.doc,.docx),Excel和Powerpoint中提取文本(字符串)。请问在哪里可以找到一个免费且简单的.NET库来读取MS Office文档? 我尝试使用NPOI,但我没有找到如何使用NPOI的示例。

10个回答

48
对于 Microsoft Word 2007 和 Microsoft Word 2010 (.docx) 文件,您可以使用 Open XML SDK。以下代码片段将打开一个文档并将其内容作为文本返回。它非常适用于任何试图使用正则表达式解析 Word 文档内容的人。要使用此解决方案,您需要引用 DocumentFormat.OpenXml.dll,该文件是 OpenXML SDK 的一部分。
请参见:http://msdn.microsoft.com/en-us/library/bb448854.aspx
 public static string TextFromWord(SPFile file)
    {
        const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

        StringBuilder textBuilder = new StringBuilder();
        using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(file.OpenBinaryStream(), false))
        {
            // Manage namespaces to perform XPath queries.  
            NameTable nt = new NameTable();
            XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
            nsManager.AddNamespace("w", wordmlNamespace);

            // Get the document part from the package.  
            // Load the XML in the document part into an XmlDocument instance.  
            XmlDocument xdoc = new XmlDocument(nt);
            xdoc.Load(wdDoc.MainDocumentPart.GetStream());

            XmlNodeList paragraphNodes = xdoc.SelectNodes("//w:p", nsManager);
            foreach (XmlNode paragraphNode in paragraphNodes)
            {
                XmlNodeList textNodes = paragraphNode.SelectNodes(".//w:t", nsManager);
                foreach (System.Xml.XmlNode textNode in textNodes)
                {
                    textBuilder.Append(textNode.InnerText);
                }
                textBuilder.Append(Environment.NewLine);
            }

        }
        return textBuilder.ToString();
    }

5
我认为目前这个回答比被采纳的回答更好,因为被采纳的回答在某些Windows版本上不起作用,而且IFilter是一个已经过时的接口。当然,在adrian发布这篇文章时情况并非如此。 - KyleM
5
SPFile是什么?你在函数中传入的参数是这种类型的,我能找到的关于它的所有内容都在Microsoft.Sharepoint.dll的Microsoft.Sharepoint命名空间中 -> 而且这个dll不容易找到。你是参考了什么才获取了SPFile? - FrenkyB
1
@user867703,您不必使用SPFile。那只是一个例子。您可以使用任何.docx文件(作为二进制流打开)。请查看WordprocessingDocument.Open方法,这是重要的方法。 - KyleM
5
我只是将SPFile更改为路径(字符串),并在open方法中仅使用了路径。解决方案非常清晰且简单。 - FrenkyB
1
在 OpenXML 包中,你需要导入以下内容:DocumentFormat.OpenXml.PackagingDocumentFormat.OpenXml.Wordprocessing。并且你需要引用 WindowsBase.dll 才能让它正常工作。除此之外,这是一个不错的解决方案。 - Kristian Barrett
显示剩余5条评论

27
使用PInvoke,可以使用IFilter接口(在Windows上)。许多常见文件类型的IFilter已经与Windows一起安装(您可以使用工具浏览它们)。您可以请求IFilter从文件中返回文本。有几组示例代码(此处是其中一个示例)。

有趣...一个非常狡猾的解决方案 :) - Skurmedel
不是真的。它是 Windows 上索引服务使用的机制,我认为桌面搜索也使用它。我曾经使用过它来索引 PDF(通过安装 Adobe IFilter - http://www.adobe.com/support/downloads/detail.jsp?ftpID=2611),所有类型的 Office 文件(这些的 IFilters 与 Windows 一起安装),以及其他几种文件类型。当它工作时,它效果很好。不过偶尔,您可能无法从 IFilter 中获取任何文本,也没有原因说明为什么。 - adrianbanks
2
我使用pInvoke并发现它非常优秀。要从任何文档中提取文本,我们只需确保机器上安装了适当的IFilter(或下载并安装)。我喜欢这篇文章和代码示例,请看这里 http://www.codeproject.com/KB/cs/IFilter.aspx 对于MS Office 2007,这是MS Office 2007过滤包 http://www.microsoft.com/downloads/details.aspx?FamilyId=60C92A37-719C-4077-B5C6-CAC34F4227CC&displaylang=en - Elias Haileselassie
是的,只要安装PDF iFilter即可。您可以通过安装Acrobat Reader(iFilter随其一起安装)或单独安装iFilter(http://www.adobe.com/support/downloads/detail.jsp?ftpID=4025)来完成此操作。[注意:还有其他PDF iFilter可用:)] - adrianbanks
请发布一个使用pInvoke调用iFilter的示例。 - paparazzo
显示剩余2条评论

18

Tika非常有帮助,可以轻松地从不同类型的文档中提取文本,包括微软办公室文件。

您可以使用此项目,该项目由Kevin Miller制作,是一件如此美妙的艺术品。http://kevm.github.io/tikaondotnet/

只需简单地添加此NuGet包https://www.nuget.org/packages/TikaOnDotNet/

然后,这一行代码就会产生魔力:

var text = new TikaOnDotNet.TextExtractor().Extract("fileName.docx  / pdf  / .... ").Text;

1
这是您需要的软件包:https://www.nuget.org/packages/TikaOnDotnet.TextExtractor/ - Russell Horwood
7
值得注意的是,这实际上是通过 IKVM(用于 Java 的 .net 运行时)运行 Apache Tika(Java)的,因此它不是轻量级解决方案(二进制文件大小为40MB,基本上是整个 Java 运行时)。 - caesay

13

让我稍微更正一下KyleM给出的答案。我添加了两个额外节点的处理,这些节点会影响结果:一个负责使用"\t"进行水平制表符,另一个负责使用"\v"进行垂直制表符。以下是代码:

    public static string ReadAllTextFromDocx(FileInfo fileInfo)
    {
        StringBuilder stringBuilder;
        using(WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(dataSourceFileInfo.FullName, false))
        {
            NameTable nameTable = new NameTable();
            XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(nameTable);
            xmlNamespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

            string wordprocessingDocumentText;
            using(StreamReader streamReader = new StreamReader(wordprocessingDocument.MainDocumentPart.GetStream()))
            {
                wordprocessingDocumentText = streamReader.ReadToEnd();
            }

            stringBuilder = new StringBuilder(wordprocessingDocumentText.Length);

            XmlDocument xmlDocument = new XmlDocument(nameTable);
            xmlDocument.LoadXml(wordprocessingDocumentText);

            XmlNodeList paragraphNodes = xmlDocument.SelectNodes("//w:p", xmlNamespaceManager);
            foreach(XmlNode paragraphNode in paragraphNodes)
            {
                XmlNodeList textNodes = paragraphNode.SelectNodes(".//w:t | .//w:tab | .//w:br", xmlNamespaceManager);
                foreach(XmlNode textNode in textNodes)
                {
                    switch(textNode.Name)
                    {
                        case "w:t":
                            stringBuilder.Append(textNode.InnerText);
                            break;

                        case "w:tab":
                            stringBuilder.Append("\t");
                            break;

                        case "w:br":
                            stringBuilder.Append("\v");
                            break;
                    }
                }

                stringBuilder.Append(Environment.NewLine);
            }
        }

        return stringBuilder.ToString();
    }

1
如果w:p中有一个图片,你该如何提取它? - Shuaib
注意:您需要添加对DocumentFormat.OpenXml的引用,并添加以下内容:using DocumentFormat.OpenXml.Packaging; - Jeff

11

使用Microsoft Office Interop。它免费且易于使用。以下是我从文档中提取所有单词的方法。

    using Microsoft.Office.Interop.Word;

   //Create Doc
    string docPath = @"C:\docLocation.doc";
    Application app = new Application();
    Document doc = app.Documents.Open(docPath);

    //Get all words
    string allWords = doc.Content.Text;
    doc.Close();
    app.Quit();

然后你可以按照自己的意愿处理这些单词。


1
啊,太棒了我的朋友。这应该是被接受的答案,其他的已经过时了。 - Hugo Nava Kopp
1
这是非常简单但也非常慢的解决方案。Open XML 快了“数千”倍。 - buks
3
“免费” - 这不需要安装 Word 吗? - Matt Burland
2
@Chris:除了Matt Burland的Catch22之外,我该如何在Linux服务器上运行它?;) - Stefan Steiger

7
现在你不需要下载任何东西,所有内容都已经随着.NET安装好了: (只需确保添加System.IO.Compression和System.IO.Compression.FileSystem的引用)。
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
using System.Text;
using System.IO.Compression;

public static class DocxTextExtractor
{
    public static string Extract(string filename)
    {
        XmlNamespaceManager NsMgr = new XmlNamespaceManager(new NameTable());
        NsMgr.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

        using (var archive = ZipFile.OpenRead(filename))
        {
            return XDocument
                .Load(archive.GetEntry(@"word/document.xml").Open())
                .XPathSelectElements("//w:p", NsMgr)
                .Aggregate(new StringBuilder(), (sb, p) => p
                    .XPathSelectElements(".//w:t|.//w:tab|.//w:br", NsMgr)
                    .Select(e => { switch (e.Name.LocalName) { case "br": return "\v"; case "tab": return "\t"; } return e.Value; })
                    .Aggregate(sb, (sb1, v) => sb1.Append(v)))
                .ToString();
        }
    }
}

这看起来是一个很好的解决方案,但我无法使其工作,因为我遇到了一个错误: “期望在中央目录结束处有条目数与中央目录中的条目数不符。” - Hugo Nava Kopp
那个消息似乎是ZipFile对于一个zip文件(在这种情况下是docx文件)损坏的概念... - lxa
1
这不起作用,因为它没有保留行尾。 - Gullbyrd

2

简单!

这两个步骤可以帮助您完成:

1)使用Office Interop库将DOC转换为DOCX
2)使用DOCX2TXT从新的DOCX中提取文本

第一步的链接有非常好的解释和代码示例。

第二步的替代方案是在C#中解压缩DOCX文件并扫描所需的文件。您可以在此处了解ZIP文件的结构

编辑:啊,是的,我忘了像Skurmedel一样指出,在您要进行转换的系统上必须安装Office。


3
使用Office交互库唯一的不足是需要先安装Office。 - Skurmedel
1
“Interop” 可以使用,但如果可能的话应该避免使用。 - Tun
Microsoft Word 12.0 Object Library --> 在我的“添加引用”右键菜单中,它不在我的“添加引用”列表中。是否有另一种方法可以输入Microsoft Word 12.0对象库,以便我可以读取Word文档? - Doug Hauf
在Godaddy托管中Interop无法工作。Godaddy不支持Office。 - Hardik Mandankaa

1

我曾经做过一个docx文本提取器,它非常简单。基本上,docx和其他(新的)格式都是一个zip文件,里面有一堆XML文件。可以使用XmlReader和仅使用.NET类来提取文本。

我不再拥有代码了,看起来:(,但我找到了一个有类似solution的人。

如果您需要读取.doc和.xls文件,则可能对您不可行,因为它们是二进制格式,可能更难解析。

还有Microsoft发布的OpenXML SDK,虽然仍处于CTP阶段。


这真的很棒!我已经完成了docx,那其他的呢? - Elias Haileselassie
你可以使用ODCB像连接数据库一样连接到xslx文件。我觉得这是一个相当繁琐的解决方案。我不知道如何读取.doc文件或.xls文件,所以我无法在这方面帮助你。 不过,这里有一个关于.xls文件的参考链接:http://sc.openoffice.org/excelfileformat.pdf - Skurmedel
很遗憾,在XLSX方面,我找不到比规范本身更好的东西:http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%201%20(PDF).zip - Skurmedel

0

如果你正在寻找asp.net选项,除非你在服务器上安装了Office,否则Interop不会起作用。即使是这样,Microsoft也建议不要这样做。

我使用了Spire.Doc,效果非常好。Spire.Doc下载 它甚至可以读取那些实际上是.txt格式但被保存为.doc的文档。他们有免费和付费版本。你还可以获得试用许可证,从你创建的文档中去除一些警告,但我并没有创建任何文档,只是搜索它们,因此免费版本完美地胜任了任务。


Erik Felde,你能给一些关于在Spire.Doc上使用ASP.NET的例子吗? - Maksud

0

在C#中从Office文档中提取文本的一个合适的选项是GroupDocs.Parser for .NET API。以下是提取简单和格式化文本的代码示例。

提取文本

// Create an instance of Parser class
using(Parser parser = new Parser("sample.docx"))
{
    // Extract a text into the reader
    using(TextReader reader = parser.GetText())
    {
        // Print a text from the document
        // If text extraction isn't supported, a reader is null
        Console.WriteLine(reader == null ? "Text extraction isn't supported" : reader.ReadToEnd());
    }
}

提取格式化文本

// Create an instance of Parser class
using (Parser parser = new Parser("sample.docx"))
{
    // Extract a formatted text into the reader
    using (TextReader reader = parser.GetFormattedText(new FormattedTextOptions(FormattedTextMode.Html)))
    {
        // Print a formatted text from the document
        // If formatted text extraction isn't supported, a reader is null
        Console.WriteLine(reader == null ? "Formatted text extraction isn't suppported" : reader.ReadToEnd());
    }
}

声明:我在GroupDocs担任开发者大使。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接