Paradigm Shift Design

ISHITOYA Kentaro's blog.

オブジェクト指向を簡単に教えるという課題が与えられたの巻き

註:課題が与えられたからといって、必ずしもそれを解いているとは限らないのです。


研究室で、先生と色々話していて、うちの研究室では

  1. 研究そのもののやり方
  2. 研究の概念や論理的な内容について
  3. 実際の実装作業

について、他の研究室と比べて遜色はないし、実は結構充実している方だということを認識した。それにそれ以外にも色々と学べるしね。


だけれども、確かに「研究」に関しては充実しているけれど、研究に欠かせない実装に関する事柄については少し疎かになっているのではないかな、と常々思っているのです。しかも、どうやら来年度からは、B4が4人、M1が2人、計6名!が新しく配属されてくるとのこと。このままじゃヤバイなという、悪寒がそこはかとなく感じられる今日この頃なのです。


まぁ、わが研究室に所属して2年経てば、確かにいっぱしに実装できるようになると思う。ねぇMさん。
でも実際には、力ずくの実装ができるだけで、そのソースコードを再利用したり拡張したりすることは難しい。ともすれば参考にすらならない場合もある。ねぇM君*1


まぁそんなわけで、今までやっていなかったのもどうかと思うけれど、1週間に一度、みんなで持ち寄ってコードリーディングなりソースレビューなりをすることにして、先週、パイロット的に第1回を行った。
ん、予想通り。ヒドイ。スサマジイ。
ひとつのクラスだったソースコードをモデルに分けたのはいい。
だけど、それを単一のファイルに全部かいてたらあんまり意味がない。
C#にはグローバル変数はないよ。それはプライベートメンバ変数というのだよ。
君がプロパティといっているのは、アクセサで、プロパティの実体は君の言うグローバル変数だよ。



…まぁそんなわけで、今週はみなさん忙しいそうなので、来週、第2回をやるのだけれど。
第1回の終了時に「オブジェクト指向を簡単に教えてくれ」と要望された。
えぇ。ほとんど何も考えずに、安請け合いしましたよ。
前置きが長くなったけれども…
オブジェクト指向を簡単に教える」
ってなんだ。そんな秘孔あったか。
カリン様の超神水飲んだってオブジェクト指向は身につかんのですよ。


普通、一般的に、オブジェクト指向を教える時って
「動物がいて〜」
「動物から派生した犬と猫がいて〜」
「犬も猫も鳴くから動物に鳴くっていう抽象メソッド作って〜」
「子クラスでオーバーライドして、『ワン』『ニャン』みたいな〜」
みたいな…
いやさ、イデア論から語るならそれもありですよ。でもねぇ。


かといってねぇ
「このソースコードを見てください!スパゲティです!」
「こういう場合は、Factoryを使うんです」
「さらにこういう場合は、Commandです」
「こんなときは、Adaptorでしょう」
みたいな…
教え方してもだれもついてこないよねぇ。


じゃなんだ、むしろ
「クラスは複数の動作と属性を一つのまとまりとして取り扱うための仕組みです。名詞でよろしく」
「動作のことをメソッドといいます。動詞でよろしく」
「属性のことをプロパティといいます。名詞でよろしく」
「継承という概念があって、これは既存のソースコードを再利用するための仕組みです」
みたいな…
基本的なところから攻めるべきなのか。


はたまた
オブジェクト指向というのは開発効率を高めるための手法です」
「派生技術としてUMLというのがあります」
「開発手法としてRADとかDSDMとかXPやTDDとかUDDとかそういうのがあります」
「開発効率を高めたり、意思疎通を図るための道具としても使われるんですね」
みたいな…
あーむり。


オブジェクト指向の分かりにくいところは、オブジェクト指向自体が技術というか言語体系というかツールとしての範疇を超えて、もうなんか概念というか、むしろ学会一つできちゃうんじゃないのみたいなところだなぁ。


それをさ「簡単に」とか、どうすればいいんですか。
とりあえずあれですか、virtualとabstractとnewとoverrideとstaticくらい教えればいいですか。
あーもー。


…安藤先生、Help!
I need somebody.
Help!
Not just anybody.
Help!
You know I need someone Help!


同じような話:
リファクタリングをするために必要なこと - Paradigm Shift Design
オブジェクト指向を教える方法を考える - Paradigm Shift Design
M1のソース - Paradigm Shift Design*2

*1:いや、多分大丈夫です、再利用させます

*2:M君の黒歴史

草津から帰ってきました

10日〜12日まで(?滋賀県)草津(?!温泉)で行われた第71回情報処理学会全国大会に行ってきました。
先に言っておきますが、全国大会を勝ち進んで世界大会出場とかありません。


僕は初日に発表で学生奨励賞を頂きました。あたくし、すでに27歳なので平均ハンデ4歳もらっていましたから微妙に複雑ですが、頂けたことは素直に嬉しかったです。あまり賞罰と関係ない世界に生きていますので、たまにはいいじゃない!


長尾研は4つのセッションで3つの賞を頂きました。残りの1つのセッションも、まぁ色々差し引いて客観的に見ても取れていたと思いますので、ちょっと悔いが残りますが。
因みに内輪ネタですが、取ったのは僕とO君と、M1のM君です。M2のM君ではありません。


M1のM君に関しては、前日の段階で微妙な感じではありましたが、夜1時まで発表練習にM2のM君と一緒に付き合って、最終的に良い発表だったと思います。


しかし、大平先生のセッションはなんだかなぁと思いました。SOA絡みのスクリーンラッピングサービスというスクレイピングをシステマチックにやりますという話は面白かったけど、その質疑応答で、デパートでラッピングみたいな話が出てて、発表者がいたたまれない感じになっていました。そもそも「インターフェース」セッションでSOAとか。


まぁそんなこんなで充実した全国大会でした。今年は発表前日に徹夜しなかったし。
去年は徹夜して発表してグダグダだったなぁ・・・

一つだけ悔いがあるとすれば、草津温泉という名の銭湯の写真を撮り忘れたってことぐらいだな。
そんなわけで。次は目指せシドニー。

はてなダイアリーの形態素数を数えるクローラー

先週・先々週あたり、TimeMachineBoardの実装で予測変換のための辞書を生成するプログラムを作っていた。長尾先生がこの間、形態素数に関して長尾のブログ2.0: 形態素を数えてみたらというエントリを書いていらっしゃったのだけれども、何かの役に立ちそうな予感がするのと、大き目のデータベースで遊んでみたかったので、はてなダイアリーのエントリーから形態素を抽出するクローラーを作ってみた。


それがなんとなく動いたので、何の意味があるかは分からないけどサマリを出してみます。


項目は以下の通り

項目名 内容
延べ形態素出現数 MeCabを利用して取得できた形態素
形態素の延べ異なり数 延べ形態素数から重複を除いた数
原型形態素の出現数 原型のみの出現数
原型形態素の異なり数 原型のみの形態素


処理は次のようにして行なっています。

  1. ダイアリからエントリを取得
  2. HTMLタグを取り除いたエントリの本文を抽出(タグに含まれるテキストは保持)
  3. MeCabで処理
  4. 記号・助詞・助動詞・アラビア数字のみの形態素を取り除く
  5. 原型のみを抽出
  6. 動詞の原型の読みを取得
  7. DBに突っ込む


とりあえず、比較対象がないと良く分からないと思いますので、青空文庫から夏目漱石の「こころ」・「三四郎」・「坊ちゃん」を持ってきてルビを削除し、TimeMachineBoard用のプログラムで、形態素数を取得してみました。

作品名 延べ形態素出現数 形態素の延べ異なり数 原型形態素の出現数 原型形態素の異なり数
こころ 150,526 61,025 49,742 5,592
三四郎 152,555 70,095 51,677 6,137
坊ちゃん 80,339 30,547 28,218 4,810
三作品合計 389,643 161,667 129,637 10,732


うーん。日本語だけでここまでいけるのはきっとすごいんだな。大体40万形態素って平均3文字として、120万文字とか・・・文筆家ってすごいな。


そして、本題のはてな。とりあえず知ってるidの人を適当に処理してみた。

id エントリ数 原型形態素の出現数 原型形態素の異なり数
id:kent013 343 57,356 10,271
id:mirakui 43 15,702 4,112
id:uzulla 472 62,539 10,881
id:atomoharu 106 7,248 2,408
id:sumi_wakhok 106 11,898 3,327
id:gi-chi 12 1,103 582
id:ono_matope 665 84,594 13,582

原型以外の形態素数が取れていないのは、前処理してからDBに入れているからです。それとエントリの取得の段階でsumi_wakhokさんの場合[Esc]から前のエントリが取れないので*1解析対象が少なくなっています。またRSSからセクションではなくて日付のエントリしか取れない場合、最後のセクションのみが対象になっています。それにgi-chiさんのエントリ数が12ってのもおかしいし、ono_matopeさんとuzullaさんの結果も参考です。
たぶん、僕とatomoharu先生とmirakuiのは正しいと思われます。


こう見ると、1年かけて夏目漱石に比肩するほどの形態素数が!と言いたいところですが。残念。僕の場合「AuthenticationInterceptor」がなぜか感動詞になっていたりするのでコードをエントリに載せている場合は形態素数が大分増えます。純粋に日本語だけで言ったら、ちゃんと処理できれば多分角さんが一番多いのじゃないかと思います。


因みに【ことばをめぐる】(991218)語彙数推計,新明解国語辞典,とっぱらう,舌足らず語彙推定テスト等でもあるように、大学生の日本語語彙力は45,000〜50,000だそうです。読めることと、それを使えることはまた別の話のようです。


で、これ何に使うの?って話ですが例えば、2つ前の僕のエントリで言えば、名詞の上位5位まで挙げてみると「リファクタリング・コード・再利用・オブジェクト指向・生産性」というのが出てきます*2。まぁとりあえず、なんとなくできたので、名詞名詞連結をしたり、特徴後抽出をしてみたり、クラスタリングしてみたり、可視化してみたり、色々と試してみながら、ごにょごにょしようとおもいます。


別に意味はないけど解析されたいという人がいたら手を挙げてください。
因みに・・・全員の感動詞の一位は当然ながら「はてな」でした。


追記:よく考えたら複合語がないから形態素数が少ないのでした。

*1:なぜか前のエントリへのリンクがループしてしまっている

*2:名詞・名詞連結は人間がやりました

Mecabで基本形の読みを取得する

いや、しかし、id:gi-chiさんにもピクルスさんにも同じことを言われてしまった。
えぇ、できれば「大学の研究」じゃなくて「研究」か「ビジネス」といわれるようなレベルにしたいんですが、今のところ全然ですね。がんばります。いやコクヨががんばってくれないかなw
相変わらず風呂敷広げっぱなしが趣味のようです。


さておき、掲題のMecabで基本形の読みを取得するという話。

盲点でした。現状では、基本形の読みは取得できません。
辞書の作成時に基本形の読みは作らず、すべて活用を展開した形での読みしか
作らないからです。

幸いにも mecab の辞書はCSVであればなんでもつっこめるので、自分で
ipadic から基本形の読みを取り出して、mecabCSV の最後のカラムに
突っ込めば情報を取り出せるようになります。

Re: 基本形の読みについて (mecab-users 184) - MeCab - SourceForge.JPと作者のくどうサンがおっしゃっていますので、Chasenと違って基本形の読みは取得できません。


というわけで辞書を変更せずに無理やり取得する方法です。
いや、要するに

  1. 形態素解析する
  2. 形態素の基本形をつなげた文字列を作る
  3. 形態素解析する
  4. マージする

という超アドホックな方法ですが、35000の形態素に対して行なって0.5秒とかそれくらいの時間で終わるので問題ないんじゃないかと思います。



まず準備として、Mecabをインストールする必要があります*1


で以下は私が使っているMecabのラッパです。そのまま使う場合は実行ディレクトリ以下にipadicをコピーするか、mecabOptionに場所を指定してください。

ipadic/dic.rcに下記フォーマットを記述

; tmboard
node-format-tmboard = %m,%f[0],%f[1],%f[2],%f[6],%f[7]\n
unk-format-tmboard  = %m,%f[0],%f[1],%f[2],%m,%m\n
eos-format-tmboard  = 

Mecab.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace com.semcode.tmboard.util
{
    public class Mecab
    {
        [DllImport("libmecab.dll")]
        extern static int mecab_new2(string arg);
        [DllImport("libmecab.dll")]
        extern static string mecab_sparse_tostr(int m, string str);
        [DllImport("libmecab.dll")]
        extern static void mecab_destroy(int m);

        public static List<MecabMorpheme> analyze(String target)
        {
            String mecabOption = "-d ipadic -O tmboard";
            int p = mecab_new2(mecabOption);
            if (p == 0)
            {
                Console.WriteLine("Mecab が見つかりません。");
                return new List<MecabMorpheme>();
            }
            String resultStr = mecab_sparse_tostr(p, target.Replace("\n", ""));
            mecab_destroy(p);

            Dictionary<String, MecabMorpheme> morphemes = new Dictionary<String, MecabMorpheme>();
            String[] splitted = resultStr.Split('\n');
            foreach (String line in splitted)
            {
                String[] data = line.Split(',');
                MecabMorpheme morpheme;
                if (data.Length == 6)
                {
                    morpheme = new MecabMorpheme(data[0], data[1], data[2], data[3], data[4], data[5]);
                }
                else if (data.Length == 3)
                {
                    morpheme = new MecabMorpheme(data[0], data[1], data[2]);
                }
                else
                {
                    continue;
                }
                if (morphemes.ContainsKey(morpheme.word))
                {
                    morphemes[morpheme.word].count++;
                }
                else
                {
                    morphemes.Add(morpheme.word, morpheme);
                }
            }

            return new List<MecabMorpheme>(morphemes.Values);
        }
    }
}

MecabMorpheme.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace com.semcode.tmboard.util
{
    public class MecabMorpheme
    {
        public static String CLASS_NOUN = "名詞";
        public static String SUBTYPE_NOUN_PROPER = "固有名詞";
        public static String SUBTYPE_NOUN_GENERAL = "一般";
        public static String SUBTYPE_NOUN_NUMERAL = "数";
        public static String CLASS_POSTPOSITION = "助詞";
        public static String CLASS_SYMBOL = "記号";


        protected String _word;
        public String word { get { return this._word; } set { this._word = value; } }
        protected String _wordClass;
        public String wordClass { get { return this._wordClass; } set { this._wordClass = value; } }
        protected String _subtype1;
        public String subtype1 { get { return this._subtype1; } set { this._subtype1 = value; } }
        protected String _subtype2;
        public String subtype2 { get { return this._subtype2; } set { this._subtype2 = value; } }
        protected double _score = 0;
        public double score { get { return this._score; } set { this._score = value; } }
        protected int _count = 0;
        public int count { get { return this._count; } set { this._count = value; } }
        protected String _yomi;
        public String yomi { get { return this._yomi; } set { this._yomi = value; } }
        protected String _original;
        public String original { get { return this._original; } set { this._original = value; } }

        public MecabMorpheme(String word, String wordClass, String subtype1)
        {
            this.word = word;
            this.wordClass = wordClass;
            this.subtype1 = subtype1;
        }

        public MecabMorpheme(String word, String wordClass, String subtype1, String subtype2, String original, String yomi)
        {
            this.word = word;
            this.wordClass = wordClass;
            this.subtype1 = subtype1;
            this.subtype2 = subtype2;
            this.yomi = yomi;
            this.original = original;
        }

        public bool isNoun()
        {
            return this.wordClass.Equals(CLASS_NOUN);
        }
        public bool isProperNoun()
        {
            return this.subtype1.Equals(SUBTYPE_NOUN_PROPER);
        }
        public bool isGeneralNoun()
        {
            return this.subtype1.Equals(SUBTYPE_NOUN_GENERAL);
        }
        public bool isSignificantNoun()
        {
            return this.isNoun() || (this.isProperNoun() || this.isGeneralNoun());
        }
        public bool isNumeralNoun()
        {
            return this.subtype1.Equals(SUBTYPE_NOUN_NUMERAL);
        }
        public bool isSymbol()
        {
            return this.wordClass.Equals(CLASS_SYMBOL);
        }
        public bool isPostPosition()
        {
            return this.wordClass.Equals(CLASS_POSTPOSITION);
        }
    }
}

でこれを使ってどうするかというと、ファイルを読み込んで助詞と記号を除いた形態素を原型で並び替えて、それに対する読みを出力しています。

        private void GenerateButton_Click(object sender, EventArgs e)
        {
            if (saveDictionaryDialog.ShowDialog() == DialogResult.OK)
            {
                List<MecabMorpheme> morphemes = new List<MecabMorpheme>();
                foreach (String filename in this.FileListBox.Items)
                {
                    morphemes.AddRange(this.ProcessFile(filename));
                }
                morphemes = this.CleanupList(morphemes);
            }
        }

        private List<MecabMorpheme> CleanupList(List<MecabMorpheme> morphemes)
        {
            Dictionary<String, MecabMorpheme> result = new Dictionary<String, MecabMorpheme>();
            foreach (MecabMorpheme morpheme in morphemes)
            {
                if (morpheme.isPostPosition() || morpheme.isSymbol())
                {
                    continue;
                }
                if (result.ContainsKey(morpheme.original))
                {
                    result[morpheme.original].count += morpheme.count;
                }
                else
                {
                    morpheme.word = morpheme.original;
                    result.Add(morpheme.original, morpheme);
                }
            }
            result = this.GetOriginalYomi(result);
            return new List<MecabMorpheme>(result.Values);
        }

        private Dictionary<String, MecabMorpheme> GetOriginalYomi(Dictionary<String, MecabMorpheme> morphemes)
        {
            String yomiStr = "";
            foreach (String original in morphemes.Keys)
            {
                yomiStr += original + " ";
            }
            List<MecabMorpheme> originals = Mecab.analyze(yomiStr);
            Dictionary<String, MecabMorpheme> yomis = new Dictionary<string, MecabMorpheme>();
            foreach (MecabMorpheme morpheme in originals)
            {
                if (yomis.ContainsKey(morpheme.original))
                {
                    continue;
                }
                yomis.Add(morpheme.original, morpheme);
            }

            foreach (MecabMorpheme morpheme in morphemes.Values)
            {
                if (yomis.ContainsKey(morpheme.original))
                {
                    morpheme.yomi = yomis[morpheme.original].yomi;
                }
            }
            return morphemes;
        }

        private List<MecabMorpheme> ProcessFile(String filename)
        {
            if (File.Exists(filename) == false)
            {
                Console.WriteLine(filename + " is not exists");
                return new List<MecabMorpheme>();
            }

            FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read);
            byte[] bytes = null;
            if(stream.Length < 1024){
                bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
            }else{
                bytes = new byte[1024];
                stream.Read(bytes, 0, bytes.Length);
            }
            Encoding encoding = TextEncoder.GetCode(bytes);

            stream.Seek(0, SeekOrigin.Begin);
            StreamReader reader = new StreamReader(stream, encoding);
            List<MecabMorpheme> result = new List<MecabMorpheme>();
            String line = "";
            while (reader.Peek() > 0)
            {
                line = reader.ReadLine();
                //line = TextEncoder.ConvertEncoding(line, encoding, Encoding.Unicode);
                result.AddRange(Mecab.analyze(line));
            }
            return result;
        }

この例では原型だけが必要だったので、原型だけ抜き出していますが必要な場合は適当にソースを書き換えてください。
あまり参考にはならないかもしれませんが

        private Dictionary<String, MecabMorpheme> GetOriginalYomi(Dictionary<String, MecabMorpheme> morphemes)
        {
            String yomiStr = "";
            foreach (String original in morphemes.Keys)
            {
                yomiStr += original + " ";
            }
            List<MecabMorpheme> originals = Mecab.analyze(yomiStr);
            Dictionary<String, MecabMorpheme> yomis = new Dictionary<string, MecabMorpheme>();
            foreach (MecabMorpheme morpheme in originals)
            {
                if (yomis.ContainsKey(morpheme.original))
                {
                    continue;
                }
                yomis.Add(morpheme.original, morpheme);
            }

            foreach (MecabMorpheme morpheme in morphemes.Values)
            {
                if (yomis.ContainsKey(morpheme.original))
                {
                    morpheme.yomi = yomis[morpheme.original].yomi;
                }
            }
            return morphemes;
        }

この関数が、原型から読みを取得しているところです。

*1:ipadicが必要なので

修士論文,提出発表完了

11月中はQCBで忙しく,12月に入ってからは論文のための実装・実験,1月は論文の執筆でてんてこ舞いでした.仕事をほっぽらかして方々にご迷惑をおかけしたと思います.この場を借りてお詫び申し上げます.


おかげさまで,修士論文を1月30日(金)に提出し終わり,2月4日(水)に発表が終わりました.
いやー・・・いやー!終わったんですよ.
2-3日実感わかなかったけど,昨日久々に部屋の掃除をして,3週間溜まりに溜まった洗濯物をして,久々に酒を飲み,かつ小説なんか読んで・・・あぁ終わったんだなぁと思いました.


反省点は色々あるけれどとりあえず終わったことで安心.しかし,3年間かけて卒業したわりにクォリティが微妙.もう少しがんばれたなぁ.ううん.


昨日まで部屋を片付ける気力がなくて,一念発起して片付けた.
f:id:kent013:20090208180542j:image:mediumf:id:kent013:20090208180543j:image:medium

使用前使用後を取ってみたけど,あまり変わり無し・・・orz


洗濯物は,洗ったり干したりする暇もなく,自分の持っている服を総動員して何とか乗り切ったと思いきや,一昨日の夜でパンツがラスイチ.5人家族かってくらい洗濯しました.


まぁそんな感じ.11月の記録を見ると72.6kg,今日計ったら70.2kg.11月の終わりからベルトの穴が2つ変わりました.論文ダイエットとか誰か言ってたけど・・・二度とやるかぁッ!・・・いや博士後期課程に行ったら,倍書くんですけどね.

新年の抱負,2009

はいはいはいはい,


新年明けましておめでとうございます!
今日から仕事始めですが,目ぇ覚めてますか!*1


しかし1ヶ月以上御無沙汰でした.
いやまぁ,Blogかけないことはないんだけれど,それより修論で泣いています.
今年は無事に卒業する予定です.


というわけで新年の抱負.
その前に去年の抱負の達成度について.
新年の抱負 - Paradigm Shift Design


去年の抱負は以下のとおりでした.

  • 形にする

MetaMovicsとQCBはある程度形になったかなと思います.

  • 読書50冊

30冊でした.

  • Blog記事200本
  • ネタ記事10本

どちらも達成しましたが,12月に記事を一つも書きませんでした.

  • 論文

現在鋭意がんばっています.

  • 自由に使えるサーバー1台.

手に入れました.

  • 英語(TOEIC800点)
  • ソフトウェア開発者
  • 数学の勉強を・・・

なにそれおいしい?という感じでした.

  • 朝起きて,夜寝る

半分達成というかんじです.今年は25-8を目標に.

  • ギター

継続はしています.

  • 山登り

しませんでした・・・orz

  • 自転車

・・・3回くらいは乗ったような.

  • 体重,72kg,体脂肪18%!(75kg/22%)

達成しました.

とまぁ,なんとなく五分五分の成績でした.



というわけで!本年の目標です.

会社

  1. MetaMovicsとQCBを本当の意味でパッケージ化する
  2. QCBをより便利に
  3. ドキュメント作成

研究

  1. Stuvie,SynvieをMetaMovicsにマージします.
  2. TimeMachineBoardの運用
  3. 国際学会論文1本・国内ジャーナル論文1本
  4. 電子ペーパーデジタルペンを用いたノートの構造化

プライベート

  1. 読書50冊
  2. 論文20本
  3. Blog記事200本
  4. ネタ記事10本
  5. 英語
  6. 数学
  7. ソフトウェア開発者
  8. ギター
  9. 登山
  10. 四国か九州か中国かニューヨークに旅行
  11. 体重75kg/15%(現在72.5kg/18%)
  12. 夜寝て,朝起きて,朝ごはんを食べる
  13. 名古屋市内に引っ越し
  14. みっくんの双子とYOGA!


性懲りもなく読書50冊とか英語とか数学とか言ってますが,きっと!今年は!
というわけで,本年も皆様どうぞよろしくお願いいたします.

*1:因みに,あたくしは徹夜していて,これを書いて寝てしまいさっき起きました・・・orz

Jsonicで画像転送API

ん、他にもいろいろ書くことはあるんだけれど、とりあえず、鍵を忘れて研究室から帰れずに増田様に来ていただくことになってしまったので、時間つぶし…orz


画像をサーバーに転送する方法はいろいろとあるみたいだけれど、Socketは使いたくないし、メールでやるのも面倒だし、HTTPがどうのとか考えたくないので、バイト配列をBase64エンコードしてJsonicで送りつけてやることにしました。


クライアント側のコードはこんな感じ

/**
 * upload background image
 */
public override void uploadBackgroundImage(Image image, int backgroundId)
{
    try
    {
        MemoryStream stream = new MemoryStream();
        Bitmap bitmap = new Bitmap((Image)image.Clone());
        bitmap.Save(stream, ImageFormat.Jpeg);

        ArrayList param = this.createRequest(Convert.ToBase64String(stream.ToArray()));
        param.Add(backgroundId);
        String result = this.call("TimeMachineBoard.uploadBackgroundImage", param);

    }
    catch (Exception e)
    {
        if (this.debug)
        {
            Console.WriteLine(e);
        }
    }
}

callとcreateRequestはなんか適当に作ったやつ。MemoryStreamを使うと簡単にbitmapのバイト配列を得ることができるらしい。
ImageConverter使うと、Byte配列は取れるけどフォーマットを指定できなくて困っていたんだけれど、MemoryStreamとBitmap.Saveで解決。
どうでもいいけど、stream.ToArrayとか増田君みたいなネーミングはやめてほしい。stream.ToBytesかToByteArrayだろ。


で、サーバー側では

/**
 * upload background image
 */
public void uploadBackgroundImage(String data, int backgroundId){
    try{
        byte[] bytes = Base64.decodeBase64(data.getBytes());
        ByteArrayInputStream input = new ByteArrayInputStream(bytes);
        BufferedImage image = ImageIO.read(input);
        FileOutputStream output = 
            new FileOutputStream("D:\\workspace\\tmboarddb\\src\\main\\webapp\\images\\" + backgroundId + ".jpg");
        ImageIO.write(image, "jpg", output);
    }catch(IOException e){			
    }
}

とかすると、データがちゃんと保存されてた。
因みに、スクリーンをキャプチャする方法 - Paradigm Shift Designで取得した画像をAPIで転送するという話でした。


しかし、普通はどうやってデータを送りつけるんだろうなぁ。

関連: