OpenWnnの内部辞書解析

OpenWnnの内部辞書を解析したのでそのメモ

経緯

OpenWnnはオープンソースの日本語変換辞書でこの派生製品がそこそこ使われているようです。NicoWnnGというソースコードをダウンロードしていじりだしたのがきっかけ。

どうしても辞書に手を入れる必要があったので調べました。NicoWnnGだけで使うのであればJava側で辞書を持っても良かったのですが,速度や他への転用を考えて,内部辞書を解析することにしました。

オープンソースとは言いつつこの辞書に関しては,バイナリを16進数配列にしたものをソースコードにベタがきしたものしか公開されておらず,このバイナリ部分を自分で作るためのソースコードは見当たりませんでした。

10年くらい前のモバイルデバイスに使われていたらしく,その時に解析された結果がいくつかあり,このサイトを参考に,ソースとにらめっこしながらようやく解析が終了しました。

使っていないであろうコードも結構見受けられたのですがなんとか。多分100%わかっているわけではないのですが,とりあえずNicoWnnGに組み込んで動くところまでは終了しました。

仕様

ヘッダ
00000000-00000003:[NJDC]識別子
00000004-00000007:バージョン
00000008-0000000b:タイプ
0000000c-0000000f:データサイズ
00000010-00000013:extサイズ
00000014-00000017:max check用(使ってない?)
00000018-0000001b:maxlen check用(使ってない?)
0000001c-0000001f: 1c-1d:前品詞数,1e-1f:後品詞数
00000020-00000023:単語ブロックのアドレス
00000024-00000027:登録されている単語の数
00000028-0000002b:登録されている単語の数?
0000002c-0000002f:que size,データ領域の一つのブロックのバイト数
00000030-00000033:最後に編集した単語ブロックの位置, 未使用
00000034-00000037:Write Flag
00000038-0000003b:未使用
0000003C-0000003F:インデックス1のアドレス
00000040-00000043:インデックス2のアドレス

インデックス
インデックス1は読み、インデックス2は表記の昇順で、
単語ブロックの位置が2バイトずつ並んでいる
最後に2バイト00がついている

単語ブロック
00000000-00000000: 2bit fflag, 4bit mflag, 6-7bit type
00000001-00000002: 1-9bit 前品詞,10-16bit 読みバイト数
00000003-00000004: 1-9bit 後品詞,10-16bit 表記バイト数
00000005- :読み、表記が詰めて設定されている
文字コードはUNICODE

フッタ
[NJDC]識別子

変換方法

ソースコードはここに入れておきました。

writedic.cppが変換用プログラムです。
入力ファイルはタブ区切りで
よみ 表記
の順に並んでいることが必要です。

一ファイルは最大65535行まで。

作成したファイルをWnnJpnDic.hのdic_07_dataなどを作成し貼り付ける。
dic_dataにもdic_07_dataを追加。プログラムから出力されるサイズをdic_sizeの該当の場所にコピー。

コンパイル

コンパイルはAndroidStudioから行う場合には文字量によるがメモリが必要なので注意。AnroidStudioのメモリ量を増やしておく必要あり

 

こんな感じで内部辞書を新しくすることができました。

CPLUS_INCLUDE_PATH

Cabochaをローカルにインストールさいのメモ

共用環境だと何かライブラリをインストールする際にも自分のローカルにインストールする必要があります。その際にハマったのでメモ

mecab

cabochaはMecabを使用しますので予めインストールしておきます。

CRF++

まず、ライブラリをインストールします。こちらから最新版をダウンロードします。現在はCRF++-0.58が最新版なのでこちらをダウンロードします。その後インストールします

$ tar xzvfp CRF++-0.58.tar.gz
$ cd CRF++-0.58
$ ./configure --prefix=~/local
$ make
$ make install

環境変数

環境変数を設定しておきます。これを設定しておかないとcabochaのコンパイル時に下記エラーが出ます。

make[1]: Entering directory `~/work/cabocha-0.69'
Making all in src
make[2]: Entering directory `~/work/cabocha-0.69/src'
/bin/sh ../libtool  --tag=CXX   --mode=compile g++ -DHAVE_CONFIG_H -I. -I.. -DCABOCHA_DEFAULT_POSSET="\"IPA"\" -DCABOCHA_DEFAULT_CHARSET="\"EUC-JP"\" -DMODEL_VERSION=102  -DCABOCHA_DEFAULT_RC="\"/work/s1630401/local/etc/cabocharc\""    -O3 -Wno-deprecated -Wall -c -o chunk_learner.lo chunk_learner.cpp
libtool: compile:  g++ -DHAVE_CONFIG_H -I. -I.. -DCABOCHA_DEFAULT_POSSET=\"IPA\" -DCABOCHA_DEFAULT_CHARSET=\"EUC-JP\" -DMODEL_VERSION=102 -DCABOCHA_DEFAULT_RC=\"/work/s1630401/local/etc/cabocharc\" -O3 -Wno-deprecated -Wall -c chunk_learner.cpp  -fPIC -DPIC -o .libs/chunk_learner.o
chunk_learner.cpp:6:19: error: crfpp.h: No such file or directory
In file included from chunk_learner.cpp:13:
chunker.h:24: error: ISO C++ forbids declaration of 'crfpp_model_t' with no type
chunker.h:24: error: expected ';' before '*' token
In file included from chunk_learner.cpp:15:
ne.h:27: error: ISO C++ forbids declaration of 'crfpp_model_t' with no type
ne.h:27: error: expected ';' before '*' token
chunk_learner.cpp: In function 'bool CaboCha::::runChunkingTrainingWithCRFPP(CaboCha::ParserType, const char*, const char*, const char*, CaboCha::CharsetType, CaboCha::PossetType, double, int)':
chunk_learner.cpp:171: error: 'crfpp_learn' was not declared in this scope
make[2]: *** [chunk_learner.lo] Error 1
make[2]: Leaving directory `~/work/cabocha-0.69/src'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `~/work/cabocha-0.69'
make: *** [all] Error 2

環境変数

$ export C_INCLUDE_PATH=~/local/include:$C_INCLUDE_PATH
$ export CPLUS_INCLUDE_PATH=$C_INCLUDE_PATH
$ export LD_LIBRARY_PATH=~/local/lib:$LD_LIBRRY_PATH
$ export PATH=~/local/bin:$PATH

cabocha

次にcabochaをインストールします。こちらから同様に最新版をダウンロードします。現在では0.69です。

$ tar zxvfp cabocha-0.69.tar.gz
$ cd cabocha-0.69
$ ./configure --prefix=~/local 
$ make
$ make install

環境変数は.bashrcなどに入れておくと面倒がなくていいです

CentOS5.11へgcc-4.8.5をインストール

たまたま余っていたCentOS5.11のサーバにpython2.7.12をインストールし、pylabをインストールしようとするとエラー。

 error: ‘SOCK_CLOEXEC’ was not declared in this scope
    error: command 'gcc' failed with exit status 1

どうもOSが古いと色々問題があります。

そこでこちらを参考にGCCをインストールします

ダウンロード

以下のサイトからダウンロードします。とりあえず最新版をダウンロードしました。

コンパイル

# mkdir /usr/local/gcc-4.8.5
# export LD_LIBRARY_PATH=/usr/local/gcc-4.8.5/lib:$LD_LIBRARY_PATH
# bunzip2 gmp-6.1.1.tar.bz2 
# tar xvfp gmp-6.1.1.tar
# cd gmp-6.1.1
# ./configure --prefix=/usr/local/gcc-4.8.5
# make && make install
# xz mpfr-3.1.4.tar.xz 
# cd mpfr-3.1.4
# ./configure --prefix=/usr/local/gcc-4.8.5 --with-gpm=/usr/local/gcc-4.8.5
# make && make install
# tar zxvfp mpc-1.0.3.tar.gz 
# cd mpc-1.0.3
# ./configure --prefix=/usr/local/gcc-4.8.5 --with-gmp=/usr/local/gcc-4.8.5 --with-mpfr=/usr/local/gcc-4.8.5
# make && make install
# tar zxvfp gcc-4.8.5.tar.gz 
# cd gcc-4.8.5
# ./configure --prefix=/usr/local/gcc-4.8.5 --enable-checking=release --with-gpm=/usr/local/gcc-4.8.5 --with-mpfr=/usr/local/gcc-4.8.5 --with-mpc=/usr/local/gcc-4.8.5 --enable-languages=c,c++
# make && make install

環境設定

使う際には、環境変数に入れて使い分けます

$ export LD_LIBRARY_PATH=/usr/local/gcc/lib:/usr/local/gcc/lib64:$LD_LIBRARY_PATH
$ export PATH=/usr/local/gcc/bin:$PATH
$ alias gcc="gcc-4.8.5"
$ alias g++="g++-4.8.5"

C言語でStringBufferもどきを作成してみる

c言語は速いのはいいのですが、いかんせん標準ライブラリ群が貧弱な面は否めません。

文字列周りの処理だとC++のSTLあたりを使用すれば簡単ですが、純粋にCで簡単に作成してみました

  • string_buffer.h
#ifndef _STRING_BUFFER_H
#define _STRING_BUFFER_H
#include 

struct string_buffer {
	unsigned int item_size;
	char *item;
	unsigned int max_item_size; // 実際のメモリ確保量
};


extern struct string_buffer *create_string_buffer();
extern void delete_string_buffer(struct string_buffer *buf);
extern int append_string_buffer(struct string_buffer *buf, char *val, unsigned int val_len);


#endif
  • string_buffer.c
#include "string_buffer.h"
#define ITEM_SIZE 1024
struct string_buffer *create_string_buffer(){
				struct string_buffer *buf;
				buf=(struct string_buffer *)malloc(sizeof(struct string_buffer));
				if(buf==NULL)return NULL;
				buf->item=(char*)malloc(sizeof(char)*ITEM_SIZE);
				if(buf->item==NULL){
								free(buf);
								return NULL;
				}
				buf->item[0]='\0';
				buf->item_size=0;
				buf->max_item_size=ITEM_SIZE;
				return buf;
}
void delete_string_buffer(struct string_buffer *buf){
				if(buf!=NULL){
								if(buf->item!=NULL) free(buf->item);
								free(buf);
				}
}
int append_string_buffer(struct string_buffer *buf, char *val, unsigned int val_len){
				int size;
				char *newbuf;
				int diff=buf->max_item_size-buf->item_size;
				if(diff>val_len){
								strncat(buf->item,val,val_len);
								buf->item_size+=val_len;
				}else{
								size=buf->max_item_size+ITEM_SIZE;
								newbuf=(char*)malloc(sizeof(char)*size);
								if(newbuf==NULL) return -1;
								newbuf[0]='\0';
								strncpy(newbuf,buf->item,buf->item_size);
								free(buf->item);
								buf->item=newbuf;
								buf->max_item_size=size;
								return append_string_buffer(buf,val,val_len);
				}
				return 0;
}

実行してみます

  • main.c

static void print_string_buffer(char *header,struct string_buffer *buf){
				printf("%s:item_size=%d,char=%s,max_item_size=%d\n",header,buf->item_size,buf->item,buf->max_item_size);
}
int main(){
				struct string_buffer *buf;
				buf=create_string_buffer();
				if(buf==NULL)exit(0);

				append_string_buffer(buf,"test",sizeof("test"));
				print_string_buffer("1",buf);

				append_string_buffer(buf," 1111",sizeof(" 1111"));
				print_string_buffer("2",buf);

				append_string_buffer(buf,"",sizeof(""));
				print_string_buffer("3",buf);

				append_string_buffer(buf,"a",sizeof("a"));
				print_string_buffer("4",buf);

				append_string_buffer(buf,"12345678901234567890",12);
				print_string_buffer("5",buf);

				append_string_buffer(buf,"123",sizeof("123"));
				print_string_buffer("6",buf);
				delete_string_buffer(buf);
}
  • 実行結果
$ a.out
1:item_size=5,char=test,max_item_size=1024
2:item_size=11,char=test 1111,max_item_size=1024
3:item_size=12,char=test 1111,max_item_size=1024
4:item_size=14,char=test 1111a,max_item_size=1024
5:item_size=26,char=test 1111a123456789012,max_item_size=1024
6:item_size=30,char=test 1111a123456789012123,max_item_size=1024

Linuxでミリ秒まで取得

ansiのC言語で用意されているtime関数では現在時刻をミリ秒まで取得できません。そこでLinuxならではの取得方法の覚え書きです

gettimeofday関数を用いてミリ秒を取得します

以下のサンプルではミリ秒単位で現在時刻を取得し、2つの時間の差を求めています

#include
#include
#include
#include
#include

struct my_tm {
	time_t tim; // yyyymmddhhmmss
	long msec;	// milli sec
};

static struct my_tm *get_now_tm(){
	struct my_tm *qt;
	struct tm *tmp;
	struct timeval tv;

	qt=(struct my_tm*)malloc(sizeof(struct my_tm));
	if(qt == NULL)return NULL;
	gettimeofday(&tv,NULL);
	tmp=localtime(&tv.tv_sec);
	qt->tim=mktime(tmp);
	qt->msec=tv.tv_usec/1000;
	printf("%04d/%02d/%02d %02d:%02d:%02d:%3d\n",
		tmp->tm_year + 1900, tmp->tm_mon + 1,
		tmp->tm_mday, tmp->tm_hour,
		tmp->tm_min, tmp->tm_sec,
		tv.tv_usec/1000);
	return qt;
}

// return milli second
static inline long my_tm_cmptime(struct my_tm* now_t,struct my_tm* prev_t){
	long diff_sec;
	long diff_msec;
	diff_sec=difftime(now_t->tim,prev_t->tim);
	diff_msec=now_t->msec-prev_t->msec;
	return diff_sec*1000+diff_msec;
}

int main(){
	int i,b;
	struct my_tm *q1;
	struct my_tm *q2;
	long dif;

	q1=get_now_tm();
	sleep(2); // 2秒あける
	b=0;
	for(i=0;i<1000000;i++) b++; // +αあける
	q2=get_now_tm();
	dif=my_tm_cmptime(q2,q1);
	printf("%d\n",dif);

	free(q1);
	free(q2);
}