Mac OS X Lionのmecabについて

広告

Mac OS X Lionが出た。さっそくインストールした。マルチタッチジェスチャは秀逸だけど古いノートでは対応していなかった。もうマウスの時代は終わった感じがする。特にノートパソコンに外付けマウスをつけて使っている人はトラックパッドの便利さを知るべきだと思う。

デスクトップの場合はMagic Trackpadがいいようだ。

スピーチ機能が強化された

あちこちでLionのレビューがなされているので、ここは少し特殊なことを書こうと思う。250の新機能があるらしいが、比較的地味なスピーチ機能が強化されている。

Lion スピーチに中国語や日本語が追加されている

Lion スピーチに中国語や日本語が追加されている

適当な中国語の文章をネットで拾って喋らせると、そこそこ上手く喋る。しかし、中国語の発音をどこに持たせているのだろうか?

ありそうなのはスピーチの音声と一緒に中国語の発音についての情報があること。もう一つはmecabの辞書が強化されていること。さて、どっちだろうか。

とりあえずmecabから調査しようと思って

mecab -d /usr/lib/mecab/dic/apple/tc/LE 

すると

tagger.cpp(153) [tokenizer_.open(*param)] tokenizer.cpp(112) [unkdic_.open(create_filename (prefix, UNK_DIC_FILE).c_str(), mode)] dictionary.cpp(50) [version_ == DIC_VERSION] incompatible version: 103

となる。あれー、以前のバージョンでは特に問題なく辞書は使えた筈なんだけど。すこしぐぐると

http://namazu.asablo.jp/blog/2009/10/03/4613273

MeCab 0.98 の辞書のフォーマットは MeCab 0.94, 0.95, 0.96, 0.97 と同じ DIC_VERSION 102 です。

DIC_VERSION と MeCab のバージョンとの関係は次の通りです。
DIC_VERSION 102, MeCab 0.94, 0.95, 0.96, 0.97, 0.98
DIC_VERSION 101, MeCab 0.92, 0.93
DIC_VERSION 100, MeCab 0.90, 0.91

0.98まではバージョン102らしい。103というのはバージョンのことか。しかし使っているmecabは最新の0.98である。さて、どうすっぺ?

バージョン103の辞書が入っていると言うことはそれに対応したmecabがMac OS X Lionのどこかにあるはずだから、それを探せばいい。

find /usr -name mecab
/usr/lib/mecab
/usr/local/bin/mecab
/usr/local/lib/mecab
/usr/local/libexec/mecab

このmecabは違う。というかlocal以下は独自ビルドを入れるところだろうし、だとすると0.98より新しいものはあるはずもない。

どうせ辞書のフォーマットも大して変わっていないだろうから、mecabのソースを手に入れてMakefileのDIC_VERSIONを103に書き換えたインチキビルドをでっちあげる。その結果、

error in mecab_new2: tagger.cpp(153) [tokenizer_.open(*param)] tokenizer.cpp(164) [n.value != -1] cannot find UNK category: DEFAULT

DEFAULTがないって。。。夜貓族で眠いから今日はここまで。Lionではどうもmecabの辞書が変わっているような気がする。気のせいでなければ。変わっているとしたら何故か。もしかしてまともな中国語の形態素解析辞書が入っているのかなー。

追記:7月21日

mecabのdictionary.cppを見て大雑把な辞書の構造は把握。それに基づいて

#include  < stdio.h>
#include  < iostream>

using namespace std;

struct Dictionary
{
	unsigned int magic;
	unsigned int version;
	unsigned int type;
	unsigned int lexsize;
	unsigned int lsize;
	unsigned int rsize;
	unsigned int dsize;
	unsigned int tsize;
	unsigned int fsize;
	unsigned int dummy;
};

static const unsigned int DictionaryMagicID = 0xef718f77u;

int main( int c, char *v[] )
{
	FILE *in = fopen( v[1], "rb" );
	Dictionary dic;
	fread( &dic, sizeof(dic), 1, in );
	
	cout << (dic.magic ^ DictionaryMagicID) << endl;
	cout << dic.version << endl;
	cout << dic.type << endl;
	cout << dic.lexsize << endl;
	cout << dic.lsize << endl;
	cout << dic.rsize << endl;
	cout << dic.dsize << endl;
	cout << dic.tsize << endl;
	cout << dic.fsize << endl;
	cout << dic.dummy << endl;
	
	char charset[32];
	fread( charset, 32, 1, in );
	
	cout << charset << endl;
	
	unsigned char *data = new unsigned char[dic.dsize];
	unsigned char *token = new unsigned char[dic.tsize];
	unsigned char *feature = new unsigned char[dic.fsize];

	fread( data, dic.dsize, 1, in );
	fread( token, dic.tsize, 1, in );
	fread( feature, dic.fsize, 1, in );

	FILE *out = fopen( "out.txt", "wt" );
	fwrite( data, dic.dsize, 1, out );
	fwrite( token, dic.tsize, 1, out );
	fwrite( feature, dic.fsize, 1, out );

	delete [] data;
	delete [] token;
	delete [] feature;
	
	fclose(out);
	fclose(in);


	return 0;
}

雑なコードだけど、これをコンパイルしてコマンドラインでsys.dicを第一引数に渡してやると

7495055
103
0
157193
390
390
1646293
2515088
3333602
0
UTF-16LE

を得る。さて問題は(わかりきっているけど)UTF-16LEであること。

そもそもC++の文字列は’’終端になっている。つまり”abcde”という文字列の場合は尻尾に’’という特殊文字がついて”abcde”になっている。’’が現れた段階で文字列は終わりになる。ところがUTF-16では’’が意図しないところで使われることもあるらしい(使ったことない)。だから普通にC++の文字列としてUTF-16を扱うと面倒なことになる。勝手に文字列の途中で終わりと判定されたりする。

たとえばdictionary.cppには次のようなコードがある。

  std::string tbuf;
  for (size_t i = 0; i < dic.size(); ++i) {
    tbuf.append(reinterpret_cast(dic[i].second),
                sizeof(Token));
    delete dic[i].second;
  }

C++のstringを使っているので’’終端である。しかし、UTF-16辞書ということは元のテキストもUTF-16であるはずで、だとしたらこのコードでは正しく扱えないことになる。もしかしてDIC_VERSION 103ってそういうことか?

つづく。