首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用.PDF、.XLS、.DOC、.PPT索引Lucene.NET

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

Stack Overflow用户
提问于 2011-02-05 05:13:37
回答 4查看 23.9K关注 0票数 34

我听说过Lucene.Net,也听说过阿帕奇蒂卡。问题是-如何使用C#和Java对这些文档进行索引?我认为问题在于,没有.Net等效的Tika从这些文档类型中提取相关文本。

更新-2011年2月5日

根据给定的响应,目前似乎不是本机 .Net等效于Tika。提到了两个有趣的项目,每个项目本身都很有趣:

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

鉴于上述两个项目,我看到了几个选择。要提取文本,我可以使用Omega使用的相同组件,也可以使用IKVM运行Tika。在我看来,选项b)看起来更干净,因为只有两个依赖项。

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

更新-2011年2月7日

另一个答案是建议我检查一下如果。事实证明,这正是微软在windows搜索中所使用的,因此Office ifilters是现成的。另外,还有一些PDF格式的文件。缺点是它们是在非托管代码中实现的,所以使用COM互操作是必要的。我在DotLucene.NET存档(不再是活动项目)上找到了下面的代码片段:

代码语言:javascript
复制
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日

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-02-05 19:19:53

如果您搜索asp.net ifilters,您还可以查看一些资源:

当然,如果您要将其分发给客户端系统,则会有额外的麻烦,因为您要么需要将ifilters包含在您的分发版中,然后将那些带有应用程序的应用程序安装到它们的计算机上,要么它们将无法从它们没有应用程序的任何文件中提取文本。

票数 6
EN

Stack Overflow用户

发布于 2011-02-05 06:57:41

这是我对Lucene的一个项目不满意的原因之一。夏皮安是一个相互竞争的产品,在某些情况下比Lucene快一个数量级,并且还具有其他引人注目的特性(嗯,它们当时对我很有吸引力)。大问题?它是用C++编写的,您必须与它互操作。这是索引和检索。对于文本的实际解析,这是Lucene真正倒下的地方--你必须自己去做。Xapian有一个omega组件,管理调用其他第三方组件来提取数据。在我有限的测试中,它运行得非常好。我没有完成这个项目(比POC更多),但是我做了写下来,我的经验编译了64位。当然,这几乎是一年前的事了,所以事情可能已经改变了。

如果深入研究欧米茄文件,您可以看到它们用于解析文档的工具。

如果pdftotext可用(随xpdf一起提供) PostScript (.ps,.eps,.ai)如果ps2pdf (来自鬼怪脚本)和pdftotext (随xpdf一起提供)是可用的 OpenOffice/StarOffice文档(.sxc、.stc、.sxd、.std、.sxi、.sti、.sxm、.sxw、.sxg、.stw) OpenDocument格式文档(.odt、.ods、.odp、.odg、.odc、.odf、.odb、.odi、.odm、.ott、.ots、.otp、.otg、.otc、.otf、.oti、.otf) MS Word文档(.doc,.dot) (如果可用的话) MS文档(.xls、.xlb、.xlt) (如果xls2csv可用)(随catdoc一起提供) 文档(.ppt,.pps),如果catppt可用,(随catdoc一起提供) MS 2007文档(.docx、.dotx、.xlsx、.xlst、.pptx、.potx、.ppsx) 如果有可用的Wordperfect文档(.wpd) (附带libwpd) 如果有可用的MS Works文档(.wps,.wpt) (随libwps一起提供) 如果gzip可用,则压缩AbiWord文档(.zabw) 如果unrtf可用,则为富文本格式文档(.rtf) Perl文档(.pl、.pm、.pod) (如果有pod2text ) 如果catdvi可用,则TeX DVI文件(.dvi) 如果djvutxt可用,DjVu文件(.djv,.djvu) 如果解压缩可用,XPS文件(.xps)

票数 4
EN

Stack Overflow用户

发布于 2011-02-05 05:59:25

显然,您可以使用.net (链接)中的Tika

我自己也没试过。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4905271

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档