2011-02-05 29 views
33

Lucene.Netについて聞いたことがありますが、Apache Tikaについて聞いたことがあります。問題は - どのようにC#とJavaを使ってこれらのドキュメントをインデックス化するのですか?問題は、これらのドキュメントタイプから関連するテキストを抽出するTikaの.Netに相当するものがないことだと思います。Lucene.NETを使用した.PDF、.XLS、.DOC、.PPTの索引付け

UPDATE - 2011年2月5日

与えられた回答に基づいて、現在ティカのネイティブの.Net等価ではないようです。 - アンマネージコードで記述されたのLuceneの代替

  1. Xapianのプロジェクトhttp://xapian.org/):2つの、興味深いプロジェクトは、自分の右に、それぞれ興味深いと述べました。このプロジェクトは、C#バインディングを可能にする "swig"をサポートすると主張しています。 Xapian Projectには、Omegaと呼ばれるすぐに使える検索エンジンがあります。 Omegaはさまざまなオープンソースコンポーネントを使用して、さまざまなドキュメントタイプからテキストを抽出します。
  2. IKVM.NEThttp://www.ikvm.net/) - Javaを.Netから実行できるようにします。 IKVMを使用してTikaを実行する例は、hereです。

上記2つのプロジェクトを考えてみると、いくつかのオプションがあります。テキストを抽出するには、a)Omegaが使用しているのと同じコンポーネントを使用するか、またはb)Tikaを実行するためにIKVMを使用するかのいずれかができます。私にとっては、オプションb)は2つの依存関係しかないので、よりクリーンであるように思えます。

興味深いのは、おそらく.Netから使用できる検索エンジンがいくつかあるということです。 Xapian、Lucene.Net、またはLucene(IKVMを使用)もあります。

UPDATE - 2011年2月7日

別の答えは私がのIFilterをチェックアウトすることを推奨して来ました。それが判明したように、これはMSがWindows検索に使用するものであり、Office ifiltersはすぐに利用可能です。また、いくつかのPDF ifiltersがあります。欠点はアンマネージコードで実装されているため、COM interopがそれらを使用する必要があることです。私は(もはやアクティブプロジェクト)DotLucene.NETアーカイブに以下のコードsnippitが見つかりました:

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プラットフォームを使用して文書からテキストを抽出するための最良の方法のように思えますサーバ。みんなあなたの助けに感謝します。

UPDATE - 2011年3月8日

私はまだのIFilterが行くには良い方法だと思いますが、私はあなたが.NETからのLuceneを使用して索引文書に探している場合、非常に良い代替をすることだと思いますSolrを使用してください。私がこのトピックの研究を始めたとき、私はSolrについて聞いたことがありませんでした。だから、あなたもそうでないあなたのために、SolrはLuceneの上にJavaで書かれた独立型の検索サービスです。この考え方は、ファイアウォールで保護されたマシンでSolrを起動し、.NETアプリケーションからHTTP経由で通信できるということです。 Solrは本当にサービスのように書かれており、Luceneが行うことができるすべてのことを行うことができます(.PDF、.XLS、.DOC、.PPTなどからのTika抽出テキストの使用を含む)。 Solrも非常にアクティブなコミュニティを持っているようですが、それはLucene.NETに関して私が確信していないことの一つです。

答えて

6

利用可能である場合にものIFilterをチェックアウトすることができます - あなたがasp.netののIFilterの検索を行う場合は、リソースの数があります

もちろん、これをクライアントシステムに配布する場合は、ディストリビューションにifilterを組み込み、自分のマシンにあなたのアプリケーションと共にインストールする必要があるか、テキストを抽出する能力がないために面倒ですそれらにはifiltersがないファイルがあります。

+0

私はこれを誰にも配布していません。それは私の会社がホストするアプリケーションのためです。これは、Windows検索の基礎となるテクノロジのように見えるので興味深いように見えるので、MSがOffice形式をサポートすることはわかっている。唯一の欠点は、COM相互運用機能を使用していることです。 – dana

+0

@dana私は、コードプロジェクトのリンクはラッパーを提供すると信じています。私はそれがEPocalipse.IFilter.dllだと思う – Prescott

3

どうやらあなたは.NETからティカを使用することができます(link

私はこれを自分で試していません。

+0

興味深い。これまでIKVMについて聞いたことはありませんが、うまくいくと思います。 LuceneのJava版を実行するためにIKVMを使用することもできますか?いずれの場合でも、チップのおかげで:) – dana

4

これは私が取り組んでいるプロジェクトのLuceneに不満を持った理由の1つです。 Xapianは競合製品であり、場合によってはLuceneよりも数桁速く、他の魅力的な機能もあります(当時私にとって魅力的でした)。大きな問題?これはC++で書かれており、あなたはそれを相互参照する必要があります。それは索引付けと検索のためのものです。テキストの実際の解析については、Luceneが本当に落ちるところです - あなたはそれを自分でやる必要があります。 Xapianには、データを抽出するために他の第三者コンポーネントを呼び出すことを管理するオメガコンポーネントがあります。私の限られたテストでは、それはかなりうまくいった。私はプロジェクト(POC以上)を終了しませんでしたが、私はwrite upを64ビット用にコンパイルしました。もちろんこれはほぼ1年前だったので、状況は変わったかもしれません。

あなたはOmega documentationに掘る場合、あなたは彼らが文書を解析するために使用するツールを見ることができます。

PDF(PDFファイル)pdftotextが利用可能な場合(xpdfのが付属しています)

のPostScript(.PS、.EPS、.AI)(ghostscriptのから)ps2pdfが及びpdftotextは(xpdfのが付属しています)場合には用意されてい

のOpenOffice/StarSuiteドキュメント(.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、。 oz)unzipが利用可能であれば

MS Word文書(.DOC、.DOT)xls2csvが利用可能な場合antiwordは

MS Excel文書(.XLS、.xlb、.XLT)が利用可能である場合(catdocが付属しています)

MSパワーポイントcatpptが利用可能な場合のドキュメント(.ppt、.pps)

MS Office 2007のドキュメント(.docx、.dlsx、.xlsx、.xlst、.pptx、.potx、.ppsx)は、unzipで利用可能

WordPerfect文書(.WPD)wpd2textが利用可能な場合は、(libwpdが付属しています)

MSワークス文書(.wps、.WPT)(libwpsが付属しています)が利用可能であるwps2text場合

圧縮AbiWordのドキュメント(.zabw)のgzipが利用可能な場合

リッチテキスト形式の文書(.RTF) unrtfが利用可能な場合

PerlのPOD文書は(.plという、.PMは、する.pod)pod2textが利用可能な場合

TeXのDVIファイル(.dvi)catdviが利用可能な場合

Djvutxtが利用可能な場合のDjVuファイル(.djv、.djvu)

XPSファイルXPS)解凍が

+0

素晴らしい書き込み。彼らは "swig"と呼ばれるものを使ってC#バインディングを生成するようです。それはあなたがあなたの覚書をして以来、あるいはそれがあなたが使ったものなのですか?また、文書処理ツールを別途ダウンロードする必要がありますか(「アンチワード」など)、Xapianに付属していますか? – dana

+0

@dana Win32バインディングファイルとビルドファイルは、http://www.flax.co.uk/xapian_binariesにあります。彼らは私がそれを見たときにJava用のswigを使用していましたが、私はそれを混乱させませんでした。チャーリー・ハルはいい男です。もしあなたが何かトラブルに遭遇すれば、ここにいます。これらのツールを個別にダウンロードすることはできます。これらはすべてオープンソースであり、Lucene(私のプロジェクトが引き出されたときに試していたものです)で使用します。したがって、アンチワードを使用して.docファイルからデータを抽出し(C#のプロセスを砲撃して)、それをLuceneに詰め込むことができます。 – Sean

2

他の角度は、Luceneインデックスがjavaと.NETの間でバイナリ互換性があることです。ですから、Tikaでインデックスを作成してC#で読むことができます。

関連する問題