2010年9月29日水曜日

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

on Mono 2.6.7


なるほど、なるほど。
  1. using Newtonsoft.Json.Linq;  
  2. using System;  
  3. using System.IO;  
  4. using System.Linq;  
  5. using System.Net;  
  6. using System.Text;  
  7. using System.Web;  
  8.   
  9. namespace Example  
  10. {  
  11.   public static class Ext  
  12.   {  
  13.     public static string GetResponseFrom(this string url)  
  14.     {  
  15.       WebRequest req = WebRequest.Create(url);  
  16.   
  17.       using (WebResponse res = req.GetResponse())  
  18.       using (Stream stm = res.GetResponseStream())  
  19.       using (StreamReader sr = new StreamReader(stm, Encoding.GetEncoding("utf-8")))  
  20.       {  
  21.         return sr.ReadToEnd();  
  22.       }  
  23.     }  
  24.   
  25.     public static string GetForConvWithGoogleTransliterate(this string hiragana)  
  26.     {  
  27.       StringBuilder url = new StringBuilder("http://www.google.com/transliterate?langpair=ja-Hira|ja&text=");  
  28.       url.Append(HttpUtility.UrlEncode(hiragana));  
  29.   
  30.       return url.ToString().GetResponseFrom();  
  31.     }  
  32.   
  33.     public static T SelectOneOf<T>(this T[] array)  
  34.     {  
  35.       for (int i = 0; i < array.Length; i++)  
  36.       {  
  37.         Console.WriteLine("{0} {1}", i + 1, array[i]);  
  38.       }    
  39.   
  40.       int n;  
  41.       do  
  42.       {  
  43.         Console.Write("Select Number> ");  
  44.         Int32.TryParse(Console.ReadLine(), out n);  
  45.       }  
  46.       while (n < 1 || n > array.Length);  
  47.   
  48.       return array[n - 1];  
  49.     }  
  50.   
  51.     public static string ToKanji(this string hiragana)  
  52.     {  
  53.       StringBuilder kanji = new StringBuilder();  
  54.       JArray jar = JArray.Parse(hiragana.GetForConvWithGoogleTransliterate());  
  55.   
  56.       foreach (JToken jt in jar)  
  57.       {  
  58.         var convArray = jt[1].ToArray();  
  59.         Console.WriteLine("{0}: {1} for conversion", jt[0], convArray.Length);  
  60.         kanji.Append((string)convArray.SelectOneOf());  
  61.       }  
  62.   
  63.       return kanji.ToString();  
  64.     }      
  65.   }  
  66.   
  67.   public class Program  
  68.   {  
  69.     public static void Main(string[] args)  
  70.     {  
  71.       Console.Write("Input Hiragana> ");  
  72.       Console.WriteLine(Console.ReadLine().ToKanji());  
  73.     }  
  74.   }  
  75. }  
  76. /* 
  77.  * Build: 
  78.  * 
  79.  *   gmcs -r:Newtonsoft.Json.dll,System.Web.dll jinput.cs 
  80.  * 
  81.  * Run: 
  82.  * 
  83.  *   mono jinput.exe 
  84.  * 
  85.  */  
$ 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

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

まず、こんなデータが返ってきます。
  1. [  
  2. [  
  3. "\u304D\u307F\u306F",  
  4. [  
  5. "\u541B\u306F","\u304D\u307F\u306F","\u30AD\u30DF\u306F","\u9EC4\u8EAB\u306F","\u738B\u306F",  
  6. ],  
  7. ],  
  8. [  
  9. "\u3058\u3064\u306B",  
  10. [  
  11. "\u5B9F\u306B","\u3058\u3064\u306B","\u5B9F\u4E8C","\u62FE\u306B","\u30B8\u30C4\u306B",  
  12. ],  
  13. ],  
  14. [  
  15. "\u3070\u304B\u3060\u306A",  
  16. [  
  17. "\u3070\u304B\u3060\u306A","\u99AC\u9E7F\u3060\u306A","\u30D0\u30AB\u3060\u306A","\u30D0\u30AB\u7530\u306A","\u3070\u304B\u3060\u306A",  
  18. ],  
  19. ],  
  20. ]  

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

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

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

になります。

以下は、デコード済みのイメージということで・・・。
  1. [  
  2. [  
  3. "きみは",  
  4. [  
  5. "君は","きみは","キミは","黄身は","王は",  
  6. ],  
  7. ],  
  8. [  
  9. "じつに",  
  10. [  
  11. "実に","じつに","実二","拾に","ジツに",  
  12. ],  
  13. ],  
  14. [  
  15. "ばかだな",  
  16. [  
  17. "ばかだな","馬鹿だな","バカだな","バカ田な","ばかだな",  
  18. ],  
  19. ],  
  20. ]  

Json.NET を使う

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

このオブジェクトを ToString() で確認してみると、
  1. [  
  2.   [  
  3.     "きみは",  
  4.     [  
  5.       "君は",  
  6.       "きみは",  
  7.       "キミは",  
  8.       "黄身は",  
  9.       "王は"  
  10.     ]  
  11.   ],  
  12.   [  
  13.     "じつに",  
  14.     [  
  15.       "実に",  
  16.       "じつに",  
  17.       "実二",  
  18.       "拾に",  
  19.       "ジツに"  
  20.     ]  
  21.   ],  
  22.   [  
  23.     "ばかだな",  
  24.     [  
  25.       "ばかだな",  
  26.       "馬鹿だな",  
  27.       "バカだな",  
  28.       "バカ田な",  
  29.       "ばかだな"  
  30.     ]  
  31.   ]  
  32. ]  

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

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

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

所感

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

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 に上げたことを追記しました。

コメントを投稿