2010年9月29日水曜日

Google 日本語入力 CGI サービスを C# で使ってみたよ

このエントリーをはてなブックマークに追加
on Mono 2.6.7


なるほど、なるほど。
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

namespace Example
{
  public static class Ext
  {
    public static string GetResponseFrom(this string url)
    {
      WebRequest req = WebRequest.Create(url);

      using (WebResponse res = req.GetResponse())
      using (Stream stm = res.GetResponseStream())
      using (StreamReader sr = new StreamReader(stm, Encoding.GetEncoding("utf-8")))
      {
        return sr.ReadToEnd();
      }
    }

    public static string GetForConvWithGoogleTransliterate(this string hiragana)
    {
      StringBuilder url = new StringBuilder("http://www.google.com/transliterate?langpair=ja-Hira|ja&text=");
      url.Append(HttpUtility.UrlEncode(hiragana));

      return url.ToString().GetResponseFrom();
    }

    public static T SelectOneOf<T>(this T[] array)
    {
      for (int i = 0; i < array.Length; i++)
      {
        Console.WriteLine("{0} {1}", i + 1, array[i]);
      }  

      int n;
      do
      {
        Console.Write("Select Number> ");
        Int32.TryParse(Console.ReadLine(), out n);
      }
      while (n < 1 || n > array.Length);

      return array[n - 1];
    }

    public static string ToKanji(this string hiragana)
    {
      StringBuilder kanji = new StringBuilder();
      JArray jar = JArray.Parse(hiragana.GetForConvWithGoogleTransliterate());

      foreach (JToken jt in jar)
      {
        var convArray = jt[1].ToArray();
        Console.WriteLine("{0}: {1} for conversion", jt[0], convArray.Length);
        kanji.Append((string)convArray.SelectOneOf());
      }

      return kanji.ToString();
    }    
  }

  public class Program
  {
    public static void Main(string[] args)
    {
      Console.Write("Input Hiragana> ");
      Console.WriteLine(Console.ReadLine().ToKanji());
    }
  }
}
/*
 * Build:
 *
 *   gmcs -r:Newtonsoft.Json.dll,System.Web.dll jinput.cs
 *
 * Run:
 *
 *   mono jinput.exe
 *
 */
$ mono jinput.exe 
Input Hiragana> きみはじつにばかだな
"きみは": 5 for conversion
1 "君は"
2 "きみは"
3 "キミは"
4 "黄身は"
5 "王は"
Select Number> 1
"じつに": 5 for conversion
1 "実に"
2 "じつに"
3 "実二"
4 "拾に"
5 "ジツに"
Select Number> 1
"ばかだな": 5 for conversion
1 "ばかだな"
2 "馬鹿だな"
3 "バカだな"
4 "バカ田な"
5 "ばかだな"
Select Number> 2
君は実に馬鹿だな

変換候補は五つで Max のようです。

以下、解説。

事前準備

Json.NET を使用しているので、その導入を行います。


ダウンロードした Json35r8.zip の「展開先/Bin/DotNet/Newtonsoft.Json.dll」を GAC に登録します。

$ sudo gacutil -i Newtonsoft.Json.dll -package 2.0
Package exported to: /usr/lib/mono/2.0/Newtonsoft.Json.dll -> ../gac/Newtonsoft.Json/3.5.0.0__30ad4fe6b2a6aeed/Newtonsoft.Json.dll
Installed Newtonsoft.Json.dll into the gac (/usr/lib/mono/gac)

-package 2.0 としたのは、ビルド時に参照指定する際、アセンブリファイル名だけで済ますためです。

$ gmcs -r:Newtonsoft.Json.dll,System.Web.dll jinput.cs

どんなデータが返ってくるの?

まず、こんなデータが返ってきます。
[
[
"\u304D\u307F\u306F",
[
"\u541B\u306F","\u304D\u307F\u306F","\u30AD\u30DF\u306F","\u9EC4\u8EAB\u306F","\u738B\u306F",
],
],
[
"\u3058\u3064\u306B",
[
"\u5B9F\u306B","\u3058\u3064\u306B","\u5B9F\u4E8C","\u62FE\u306B","\u30B8\u30C4\u306B",
],
],
[
"\u3070\u304B\u3060\u306A",
[
"\u3070\u304B\u3060\u306A","\u99AC\u9E7F\u3060\u306A","\u30D0\u30AB\u3060\u306A","\u30D0\u30AB\u7530\u306A","\u3070\u304B\u3060\u306A",
],
],
]

デコードする必要がありますが、その前に \uxxxx を %uxxxx に修正する必要があります。わざわざデコードする必要はなく、Json.NET の方で対応していました。よって以下のコードは、

Before:
string jsonArray = url.ToString().GetResponseFrom().Replace(@"\", "%");
return HttpUtility.UrlDecode(jsonArray);

After:
return url.ToString().GetResponseFrom();

になります。

以下は、デコード済みのイメージということで・・・。
[
[
"きみは",
[
"君は","きみは","キミは","黄身は","王は",
],
],
[
"じつに",
[
"実に","じつに","実二","拾に","ジツに",
],
],
[
"ばかだな",
[
"ばかだな","馬鹿だな","バカだな","バカ田な","ばかだな",
],
],
]

Json.NET を使う

JSON 形式の配列を、Json.NET から提供される JArray オブジェクトに変換します。
JArray jar = JArray.Parse(hiragana.GetForConvWithGoogleTransliterate());

このオブジェクトを ToString() で確認してみると、
[
  [
    "きみは",
    [
      "君は",
      "きみは",
      "キミは",
      "黄身は",
      "王は"
    ]
  ],
  [
    "じつに",
    [
      "実に",
      "じつに",
      "実二",
      "拾に",
      "ジツに"
    ]
  ],
  [
    "ばかだな",
    [
      "ばかだな",
      "馬鹿だな",
      "バカだな",
      "バカ田な",
      "ばかだな"
    ]
  ]
]

おおお、インデンドされて、分かりやすくなりました。

上記の例だと、「きみはじつにばかだな」が「きみは」「じつに」「ばかだな」の3文節に分かれて、それぞれが JToken オブジェクトとして扱えます。

それぞれの JToken オブジェクト(トークン)は、もとの文字列を保持するトークン、変換候補を保持するトークンで構成され、さらに変換候補保持トークンの中は、変換候補一個一個のトークンという構成になります。
foreach (JToken jt in jar)
{
  var convArray = jt[1].ToArray();
  Console.WriteLine("{0}: {1} for conversion", jt[0], convArray.Length);
  kanji.Append((string)convArray.SelectOneOf());
}

所感

実際の使い所はどの辺になるのでしょうか?

2012.06.19 追記:
少し改修して、GitHub に上げてみました。


4 件のコメント:

sta さんのコメント...

追加:
public static string GetResponseFrom(this string url)

変更:
Before: public static T SelectOne<T>(this T[] array)

After: public static T SelectOneOf<T>(this T[] array)

に伴う修正を行ないました。

sta さんのコメント...

返ってきた JSON 形式データへのデコードに関する記述を修正しました。

sta さんのコメント...

Before:
public static string GetResponseFromGoogleTransliterate(this string hiragana)

public static string TranslateHira(this string hiragana)

After:
public static string GetForConvWithGoogleTransliterate(this string hiragana)

public static string ToKanji(this string hiragana)

に伴う変更を行ないました。

sta さんのコメント...

GitHub に上げたことを追記しました。

コメントを投稿