検索エンジンを作る

第16回 テキスト情報の抽出[その3]

この記事を読むのに必要な時間:およそ 6.5 分

IFilter経由でのテキスト情報の抽出

最後にIFilterを使って,テキスト情報を抽出するサンプルプログラムを紹介しましょう。 次のPreviewResults.csは,C#のプログラムからIFilterのインターフェースを呼び出して,文書ファイルからテキスト情報を抜き出すプログラムです。GetIFilterResults(string path_)というメソッドが,IFilterを通じてテキスト情報を抽出している箇所です。

このクラスはHTMLのレンダリング機能や検索したテキストの箇所を赤い色でハイライトする機能も持っており,検索結果をプレビューする機能としても利用できます。

リスト PreviewResults.cs

using System;
using System.Data;
using System.Text.RegularExpressions;
using System.Web.UI;

namespace Sample
{
  /// <summary>
  /// PreviewResults class exstructs the contents of a document with a proper IFilter.
  /// </summary>
  public class PreviewResults : Control
  {
    /// <summary>
    /// path of the document.
    /// </summary>
    private string path;
    /// <summary>
    /// text contents of the document.
    /// </summary>
    private string contents;
    /// <summary>
    /// search text
    /// </summary>
    private string search_text;
    /// <summary>
    /// set path of the document.
    /// </summary>
    /// <param name="x">
    /// path
    /// </param>
    void SetPath(string x)
    {
      path = x;
    }
    public void SetSearchText(string x)
    {
      search_text = x;
    }
    /// <summary>
    /// clear text contents.
    /// </summary>
    void ClearContents()
    {
      contents = "";
    }
    /// <summary>
    /// constructor.
    /// </summary>
    public PreviewResults()
    {
      path = new String(' ', 0);
      search_text = new String(' ', 0);
      ClearContents();
    }
    public bool GetIFilterResults(string path_)
    {
      path = path_;
      try
      {
        IFilter ifilt = (IFilter)(new CFilter());
        System.Runtime.InteropServices.UCOMIPersistFile ipf = 
          (System.Runtime.InteropServices.UCOMIPersistFile)(ifilt);
        ipf.Load(path, 0);
        uint i = 0;
        STAT_CHUNK ps = new STAT_CHUNK();
        ifilt.Init(0, 0, null, ref i);
        int hr = 0;                   
        while (hr == 0)
        {
          ifilt.GetChunk(out ps);
          if (ps.flags == CHUNKSTATE.CHUNK_TEXT)
          {
            uint pcwcBuffer = 1000;
            int hr2 = 0;
            while (hr2 == Constants.FILTER_S_LAST_TEXT || hr2 == 0)
            {
              pcwcBuffer = 1000;
              System.Text.StringBuilder sbBuffer = new System.Text.StringBuilder((int)pcwcBuffer);
              hr2 = ifilt.GetText(ref pcwcBuffer, sbBuffer);
              //Console.Write(sbBuffer.ToString(0, (int)pcwcBuffer));
              contents += sbBuffer.ToString(0, (int)pcwcBuffer);
            }
          }
        }
      }
      catch (System.Exception /*ex*/)
      {
        //Console.WriteLine(ex.Message);
        return false;
      }
      return true;
    }
    /// <summary>
    /// render the HTML contents.
    /// </summary>
    /// <param name="writer"></param>
    protected override void Render(HtmlTextWriter writer)
    {
      writer.Write("<h1>");
      writer.Write(path);
      writer.Write("</h1>");
      writer.Write("<table bgcolor=\"#e8e8e8\">");
      writer.Write("<td>");
      RenderContents(writer);
      writer.Write("</td>");
      writer.Write("</table>");
    }
    protected void RenderContents(HtmlTextWriter writer)
    {
      string s1 = new String(' ', 0);
      s1 = contents;
      // .NETの文字列メソッドが遅いので配列を使って回避します。
      int s1_len = s1.Length;
      char[] x = new char[s1_len * 6 + 1];  // MAXで の6倍の長さに展開されるため
      int xi = 0;
      int n;
      bool top_p = true;
      for (n = 0; n < s1_len; n++)
      {
        char c = s1[n];
        switch (c)
        {
          case '&':  // -> "&"
            x[xi++] = '&';
            x[xi++] = 'a';
            x[xi++] = 'm';
            x[xi++] = 'p';
            x[xi++] = ';';
            break;
          case '<':  // -> "<"
            x[xi++] = '&';
            x[xi++] = 'l';
            x[xi++] = 't';
            x[xi++] = ';';
            break;
          case '>':  // -> ">"
            x[xi++] = '&';
            x[xi++] = 'g';
            x[xi++] = 't';
            x[xi++] = ';';
            break;
          case '\r':
            break;
          case '\n':  // -> "<br>\n"
            top_p = true;
            x[xi++] = '<';
            x[xi++] = 'b';
            x[xi++] = 'r';
            x[xi++] = '>';
            x[xi++] = '\n';
            break;
          case '\t':
          case ' ':
            if (top_p)
            {
              // -> " "
              x[xi++] = '&';
              x[xi++] = 'n';
              x[xi++] = 'b';
              x[xi++] = 's';
              x[xi++] = 'p';
              x[xi++] = ';';
            }
            else
            {
              // -> " "
              x[xi++] = ' ';
            }
            break;
          default:
            top_p = false;
            x[xi++] = c;
            break;
        }
      }
      x[xi] = '\0';
      char[] y = new char[xi];
      for (n = 0; n < xi; n++)
      {
        y[n] = x[n];
      }
      string s2 = new String(y);
      if (search_text.Length > 0)
      {
        s2 = s2.Replace(search_text, "<span style=\"color:red; font-weight:bold\">" + search_text + "</span>");
      }
      writer.Write(s2);      
    }
  }
}

著者プロフィール

工藤智行(くどうともゆき)

有限会社サイパック取締役社長。システム構築・管理のコンサルティング,ローカライゼーション,文書処理や障害者向けソフトウェアを中心とするプログラミングを長年手がける。 近著『UNIXプログラミングの道具箱』『システム管理現場の鉄則FreeBSD編』等

URLhttp://www.cypac.co.jp/

著書