.net - 使用 Lucene.NET 索引 .PDF、.XLS、.DOC、.PPT

标签 .net asp.net lucene solr lucene.net

我听说过Lucene.Net我听说过Apache Tika .问题是 - 我如何使用 C# vs Java 索引这些文档?我认为问题在于没有与 Tika 等效的 .Net 从这些文档类型中提取相关文本。

更新 - 2011 年 2 月 5 日

根据给定的回复,目前似乎不是 原生 .Net 相当于 Tika。提到了 2 个有趣的项目,每个项目本身都很有趣:

  • 厦片项目 ( http://xapian.org/ ) - 用非托管代码编写的 Lucene 的替代方案。该项目声称支持允许 C# 绑定(bind)的“swig”。在 Xapian 项目中有一个开箱即用的搜索引擎,称为 Omega。 Omega 使用各种开源组件从各种文档类型中提取文本。
  • IKVM.NET ( http://www.ikvm.net/ ) - 允许从 .Net 运行 Java。可以找到使用 IKVM 运行 Tika 的示例 here .

  • 鉴于上述 2 个项目,我看到了几个选项。要提取文本,我可以 a) 使用 Omega 使用的相同组件,或者 b) 使用 IKVM 运行 Tika。对我来说,选项 b) 似乎更干净,因为只有 2 个依赖项。

    有趣的是,现在有几个搜索引擎可能可以从 .Net 中使用。有 Xapian、Lucene.Net 甚至 Lucene(使用 IKVM)。

    更新 - 2011 年 2 月 7 日

    另一个答案是建议我检查 ifilters。事实证明,这是 MS 用于 Windows 搜索的,因此 Office ifilter 很容易获得。此外,还有一些 PDF ifilter。缺点是它们是在非托管代码中实现的,因此需要 COM 互操作才能使用它们。我在 DotLucene.NET 存档中找到了以下代码片段(不再是事件项目):
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace IFilter
    {
        [Flags]
        public enum IFILTER_INIT : uint
        {
            NONE = 0,
            CANON_PARAGRAPHS = 1,
            HARD_LINE_BREAKS = 2,
            CANON_HYPHENS = 4,
            CANON_SPACES = 8,
            APPLY_INDEX_ATTRIBUTES = 16,
            APPLY_CRAWL_ATTRIBUTES = 256,
            APPLY_OTHER_ATTRIBUTES = 32,
            INDEXING_ONLY = 64,
            SEARCH_LINKS = 128,
            FILTER_OWNED_VALUE_OK = 512
        }
    
        public enum CHUNK_BREAKTYPE
        {
            CHUNK_NO_BREAK = 0,
            CHUNK_EOW = 1,
            CHUNK_EOS = 2,
            CHUNK_EOP = 3,
            CHUNK_EOC = 4
        }
    
        [Flags]
        public enum CHUNKSTATE
        {
            CHUNK_TEXT = 0x1,
            CHUNK_VALUE = 0x2,
            CHUNK_FILTER_OWNED_VALUE = 0x4
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct PROPSPEC
        {
            public uint ulKind;
            public uint propid;
            public IntPtr lpwstr;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct FULLPROPSPEC
        {
            public Guid guidPropSet;
            public PROPSPEC psProperty;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct STAT_CHUNK
        {
            public uint idChunk;
            [MarshalAs(UnmanagedType.U4)] public CHUNK_BREAKTYPE breakType;
            [MarshalAs(UnmanagedType.U4)] public CHUNKSTATE flags;
            public uint locale;
            [MarshalAs(UnmanagedType.Struct)] public FULLPROPSPEC attribute;
            public uint idChunkSource;
            public uint cwcStartSource;
            public uint cwcLenSource;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct FILTERREGION
        {
            public uint idChunk;
            public uint cwcStart;
            public uint cwcExtent;
        }
    
        [ComImport]
        [Guid("89BCB740-6119-101A-BCB7-00DD010655AF")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IFilter
        {
            [PreserveSig]
            int Init([MarshalAs(UnmanagedType.U4)] IFILTER_INIT grfFlags, uint cAttributes, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] FULLPROPSPEC[] aAttributes, ref uint pdwFlags);
    
            [PreserveSig]
            int GetChunk(out STAT_CHUNK pStat);
    
            [PreserveSig]
            int GetText(ref uint pcwcBuffer, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder buffer);
    
            void GetValue(ref UIntPtr ppPropValue);
            void BindRegion([MarshalAs(UnmanagedType.Struct)] FILTERREGION origPos, ref Guid riid, ref UIntPtr ppunk);
        }
    
        [ComImport]
        [Guid("f07f3920-7b8c-11cf-9be8-00aa004b9986")]
        public class CFilter
        {
        }
    
        public class IFilterConstants
        {
            public const uint PID_STG_DIRECTORY = 0x00000002;
            public const uint PID_STG_CLASSID = 0x00000003;
            public const uint PID_STG_STORAGETYPE = 0x00000004;
            public const uint PID_STG_VOLUME_ID = 0x00000005;
            public const uint PID_STG_PARENT_WORKID = 0x00000006;
            public const uint PID_STG_SECONDARYSTORE = 0x00000007;
            public const uint PID_STG_FILEINDEX = 0x00000008;
            public const uint PID_STG_LASTCHANGEUSN = 0x00000009;
            public const uint PID_STG_NAME = 0x0000000a;
            public const uint PID_STG_PATH = 0x0000000b;
            public const uint PID_STG_SIZE = 0x0000000c;
            public const uint PID_STG_ATTRIBUTES = 0x0000000d;
            public const uint PID_STG_WRITETIME = 0x0000000e;
            public const uint PID_STG_CREATETIME = 0x0000000f;
            public const uint PID_STG_ACCESSTIME = 0x00000010;
            public const uint PID_STG_CHANGETIME = 0x00000011;
            public const uint PID_STG_CONTENTS = 0x00000013;
            public const uint PID_STG_SHORTNAME = 0x00000014;
            public const int FILTER_E_END_OF_CHUNKS = (unchecked((int) 0x80041700));
            public const int FILTER_E_NO_MORE_TEXT = (unchecked((int) 0x80041701));
            public const int FILTER_E_NO_MORE_VALUES = (unchecked((int) 0x80041702));
            public const int FILTER_E_NO_TEXT = (unchecked((int) 0x80041705));
            public const int FILTER_E_NO_VALUES = (unchecked((int) 0x80041706));
            public const int FILTER_S_LAST_TEXT = (unchecked((int) 0x00041709));
        }
    
        /// 
        /// IFilter return codes
        /// 
        public enum IFilterReturnCodes : uint
        {
            /// 
            /// Success
            /// 
            S_OK = 0,
            /// 
            /// The function was denied access to the filter file. 
            /// 
            E_ACCESSDENIED = 0x80070005,
            /// 
            /// The function encountered an invalid handle, probably due to a low-memory situation. 
            /// 
            E_HANDLE = 0x80070006,
            /// 
            /// The function received an invalid parameter.
            /// 
            E_INVALIDARG = 0x80070057,
            /// 
            /// Out of memory
            /// 
            E_OUTOFMEMORY = 0x8007000E,
            /// 
            /// Not implemented
            /// 
            E_NOTIMPL = 0x80004001,
            /// 
            /// Unknown error
            /// 
            E_FAIL = 0x80000008,
            /// 
            /// File not filtered due to password protection
            /// 
            FILTER_E_PASSWORD = 0x8004170B,
            /// 
            /// The document format is not recognised by the filter
            /// 
            FILTER_E_UNKNOWNFORMAT = 0x8004170C,
            /// 
            /// No text in current chunk
            /// 
            FILTER_E_NO_TEXT = 0x80041705,
            /// 
            /// No more chunks of text available in object
            /// 
            FILTER_E_END_OF_CHUNKS = 0x80041700,
            /// 
            /// No more text available in chunk
            /// 
            FILTER_E_NO_MORE_TEXT = 0x80041701,
            /// 
            /// No more property values available in chunk
            /// 
            FILTER_E_NO_MORE_VALUES = 0x80041702,
            /// 
            /// Unable to access object
            /// 
            FILTER_E_ACCESS = 0x80041703,
            /// 
            /// Moniker doesn't cover entire region
            /// 
            FILTER_W_MONIKER_CLIPPED = 0x00041704,
            /// 
            /// Unable to bind IFilter for embedded object
            /// 
            FILTER_E_EMBEDDING_UNAVAILABLE = 0x80041707,
            /// 
            /// Unable to bind IFilter for linked object
            /// 
            FILTER_E_LINK_UNAVAILABLE = 0x80041708,
            /// 
            /// This is the last text in the current chunk
            /// 
            FILTER_S_LAST_TEXT = 0x00041709,
            /// 
            /// This is the last value in the current chunk
            /// 
            FILTER_S_LAST_VALUES = 0x0004170A
        }
    
        /// 
        /// Convenience class which provides static methods to extract text from files using installed IFilters
        /// 
        public class DefaultParser
        {
            public DefaultParser()
            {
            }
    
            [DllImport("query.dll", CharSet = CharSet.Unicode)]
            private extern static int LoadIFilter(string pwcsPath, [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, ref IFilter ppIUnk);
    
            private static IFilter loadIFilter(string filename)
            {
                object outer = null;
                IFilter filter = null;
    
                // Try to load the corresponding IFilter
                int resultLoad = LoadIFilter(filename,  outer, ref filter);
                if (resultLoad != (int) IFilterReturnCodes.S_OK)
                {
                    return null;
                }
                return filter;
            }
    
            public static bool IsParseable(string filename)
            {
                return loadIFilter(filename) != null;
            }
    
            public static string Extract(string path)
            {
                StringBuilder sb = new StringBuilder();
                IFilter filter = null;
    
                try
                {
                    filter = loadIFilter(path);
    
                    if (filter == null)
                        return String.Empty;
    
                    uint i = 0;
                    STAT_CHUNK ps = new STAT_CHUNK();
    
                    IFILTER_INIT iflags =
                        IFILTER_INIT.CANON_HYPHENS |
                        IFILTER_INIT.CANON_PARAGRAPHS |
                        IFILTER_INIT.CANON_SPACES |
                        IFILTER_INIT.APPLY_CRAWL_ATTRIBUTES |
                        IFILTER_INIT.APPLY_INDEX_ATTRIBUTES |
                        IFILTER_INIT.APPLY_OTHER_ATTRIBUTES |
                        IFILTER_INIT.HARD_LINE_BREAKS |
                        IFILTER_INIT.SEARCH_LINKS |
                        IFILTER_INIT.FILTER_OWNED_VALUE_OK;
    
                    if (filter.Init(iflags, 0, null, ref i) != (int) IFilterReturnCodes.S_OK)
                        throw new Exception("Problem initializing an IFilter for:\n" + path + " \n\n");
    
                    while (filter.GetChunk(out ps) == (int) (IFilterReturnCodes.S_OK))
                    {
                        if (ps.flags == CHUNKSTATE.CHUNK_TEXT)
                        {
                            IFilterReturnCodes scode = 0;
                            while (scode == IFilterReturnCodes.S_OK || scode == IFilterReturnCodes.FILTER_S_LAST_TEXT)
                            {
                                uint pcwcBuffer = 65536;
                                System.Text.StringBuilder sbBuffer = new System.Text.StringBuilder((int)pcwcBuffer);
    
                                scode = (IFilterReturnCodes) filter.GetText(ref pcwcBuffer, sbBuffer);
    
                                if (pcwcBuffer > 0 && sbBuffer.Length > 0)
                                {
                                    if (sbBuffer.Length < pcwcBuffer) // Should never happen, but it happens !
                                        pcwcBuffer = (uint)sbBuffer.Length;
    
                                    sb.Append(sbBuffer.ToString(0, (int) pcwcBuffer));
                                    sb.Append(" "); // "\r\n"
                                }
    
                            }
                        }
    
                    }
                }
                finally
                {
                    if (filter != null) {
                        Marshal.ReleaseComObject (filter);
                        System.GC.Collect();
                        System.GC.WaitForPendingFinalizers();
                    }
                }
    
                return sb.ToString();
            }
        }
    }
    

    目前,这似乎是在 Windows 服务器上使用 .NET 平台从文档中提取文本的最佳方式。谢谢大家的帮助。

    更新 - 2011 年 3 月 8 日

    虽然我仍然认为 ifilter 是一个不错的选择,但我认为如果您希望使用 .NET 中的 Lucene 来索引文档,一个很好的选择是使用 Solr .当我第一次开始研究这个主题时,我从未听说过 Solr。所以,对于那些还没有的人,Solr 是一个独立的搜索服务,在 Lucene 之上用 Java 编写。这个想法是您可以在防火墙机器上启动 Solr,并通过 HTTP 从您的 .NET 应用程序与其通信。 Solr 真的像服务一样编写,可以做 Lucene 可以做的所有事情,(包括使用 Tika 从 .PDF、.XLS、.DOC、.PPT 等中提取文本)等等。 Solr 似乎也有一个非常活跃的社区,关于 Lucene.NET,这是我不确定的一件事。

    最佳答案

    您还可以查看 ifilters - 如果您搜索 asp.net ifilters,则有许多资源:

  • http://www.codeproject.com/KB/cs/IFilter.aspx
  • http://en.wikipedia.org/wiki/IFilters
  • http://www.ifilter.org/
  • https://stackoverflow.com/questions/1535992/ifilter-or-sdk-for-many-file-types

  • 当然,如果您将其分发到客户端系统会增加麻烦,因为您需要在您的分发中包含 ifilter 并将它们与您的应用程序一起安装在他们的机器上,或者他们将无法从任何文件中提取文本他们没有 ifilters 。

    关于.net - 使用 Lucene.NET 索引 .PDF、.XLS、.DOC、.PPT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4905271/

    相关文章:

    c# - RDLC 报告在导出为 asp.net 中的任何格式时不显示报告名称

    grails - 将 grails 应用程序升级到 2.3.7 时出错

    c# - 从子控件检测 ViewDidUnload

    asp.net - DotNet Pack 什么都不做

    .net - NuGet 更新和条件引用

    c# - 月份和年份的日期时间格式

    lucene - 我如何在 Lucene 中进行实体提取

    lucene - 具有开始和结束时间的 ElasticSearch 直方图和 Kibana

    c# - 警报在 Chrome 驱动程序中不起作用

    .net - 有谁知道如何让 WPF MessageBox 看起来更好(比如 Windows Forms equivelant)?