日本語形態素解析システムJUMANを、WindowsでUTF-8が使えるようにビルドしました。
そのバイナリ配布と、ビルドから配布まででハマった点について記します。
はじめに
AI的日本語処理事始めにおいて、まず分かち書きをどうしようか考えます。
DeepAgeさんの「MeCabより高精度?RNNLMの形態素解析器JUMAN++」を読むと、JUMAN++が、スピードは遅いものの精度は良いようです。しかし黒橋・河原研究室「日本語形態素解析システム JUMAN++」からパッケージをダウンロードしてみましたが、Windowsにはまだ対応していません。
そこでJUMAN++の前身「日本語形態素解析システム JUMAN」を見ました。Windows版バイナリもこのページからダウンロード可能です。
しかしその入出力はShift_JISです。しかし一連の処理はUTF-8でしたいところ。
スクリプト側でエンコーディングの変換は可能と思いますが、その都度入出力で変換するのは非効率です。それにマッピングで壊れる文字もあるはず。(波ダッシュとか。)
そこでUTF-8に対応させるために、ソースからビルドしてみることにしました。
結果、ビルドできました。
文字コードに関するところは弄っていませんので、もしかしたら辞書ファイル等をUFT-8にするだけで解決したのかもしれません。
(それでも結局は辞書の構築ツールのビルドが必要なのですが。)
バイナリの配布
需要があるかはわかりませんが、バイナリを配布することにしました。
こちらからjuman-7.01_patched-win64.zipをクリックしてダウンロードしてください。
ZIPを解凍し、適当なディレクトリに配置するだけです。
簡単な説明はこちらからどうぞ。
これだけだと寂しいので、実行レポート。
エクスプローラーから、解凍したディレクトリ内にある「juman-i.bat
」をダブルクリックすると、コマンドプロンプトが立ち上がります。
文章を入力してEnterを押すと、解析結果を表示します。
…って、公式リリース版と同じですね。
juman-i.bat
の中身には「juman --enc cp932
」とだけ書いています。
オプションの「--enc
」は魔改造したもので、わざわざCP932(Windows専用ほぼShift_JISのコードページ)を指定しています。
未指定の場合は、UTF-8で動きますので動くので文字化けします。
試しに素のjuman.exe
をダブルクリックしてみます。
このjumanさんは、入力をUTF-8として解析しようとして間違えます。また解析結果もUTF-8で出力するので、CP932のコマンドプロンプトでは文字化けします。
ちなみにMSYS2のコンソールはデフォルトでUTF-8ですから、juman.exeのエンコード指定なしで正常動作します。
実際の使用には、他のプログラムからのパイプか、或いはファイル入出力で使用するでしょうから、UTF-8で問題ありません。
ビルドから頒布まで
これだけだとブログ記事として寂しいので、ビルドから配布まででハマった点などを記しておきます。
configureのオプション
MSYS2のgccで普通にビルドすると、必要なライブラリはデフォルトでダイナミックリンクのライブラリ(つまりDLL)になってしまいます。
必要なライブラリを実行ファイルと同じディレクトリに持ってこない限り、MSYS2のコンソール以外からはエラーが出て起動できません。
必要なDLLをコピーしても良いのですが、美しくありません。DLLに対応するスタティクリンクのライブラリもあるので、スタティックリンクは出来るはず。
ということで、結果はこのようなconfigureのオプションにしてスタティックリンクできました。
./configure LDFLAGS="-Wl,-Bstatic" LIBS="-lregex -ltre -lintl -liconv"
(--prefix
は省略。)
コンパイルとリンクがlibtool経由なので、正しい指定がいま一つわかりません。-static
は消されてダイナミックリンクにされるし、-all-static
はconfigureでエラーになるし。
結局上のようなLDFLAGSでいけました。LIBSはリンクエラーを見て、参照される順に並べてあげました。
Cの標準ライブラリはWindowsのDLL(VCの再頒布パッケージ)を使うのでリンクは不要なのですね。なるほど。
デバッグ環境の作成
ビルドできたら終わりで良かったのですが、実行するとSegment Faultがでます。
そこでもうデバッグの必要性。
コンパイルオプションに「-g-O0
」も指定してmakeし直すものの、gdbはちょっと…。
もしやと思ってGoogle先生に尋ねると、Visual Stadio CodeにC/C++用のアドオンもある模様。
Pythonのときと同様、アドオンを「C/C++」と検索してインストールするだけで簡単にデバッグ環境ができました。
gdbのパスはmsys64/mingw/64/bin/gdb.exe
でOK。
jumanrcについて
デバッガで追ってみると、どうやらWindowsのディレクトリにある「juman.ini」を読んで、インストールした公式リリース版のShift_JISの設定ファイル(jumanrc)を読んでおかしくなっているようです。
これは簡単に、iniを読むWin32API、GetPrivateProfileStringのコールをマクロで変えて対応。デフォルトのjumanrcの場所も、このマクロの中でexeのある場所を返すようにします。UNIX版が$HOME/.jumanrc
を参照する代わりですね。
(juman.iniに書かれたディレクトリへのパスは、辞書ファイルの場所としても参照されるのですが、そのときGetPrivateProfileStringのマクロは空文字列を返すようにしています。)
これでjumanrcを読み込むようになりましたが、その中の辞書ファイルの場所の記述が絶対パスだと、パッケージのディレクトリを移動した際に書き換えが必要です。
このことをそのうち忘れ、ある日Segmentation Faultで悩むような気がしますので、jumanrcファイルからの相対パスにしました。(これが魔改造の始まり。カレントディレクトリを調べたり、実行ファイルのディレクトリから調べる機能はオミット。)
尚、辞書ファイルがうまく読めなかった場合などSegmentation Faultが出がちですが、そこは追ってもいません。
2017年9月26日追記:ちょっと追ってみました。grammar.cの関数grammar()の中で読んでいる、iotool.cのerror()の中でmy_exit()を呼んだ先でSegmentation Faultが起きています。
プログラム的におかしいところはないので、grammar()の中で読んでいるpathfopen()の中でgrammarfile_pathをバッファーオーバーランしてスタックを壊しているのかなあ、と推測。通らないように改造。
2017年10月25日追記:error()の引数の宣言「int errno」のerrnoが<errno.h> のerrnoと被っていて宣言通りのint型になっていないようです。これが関係しているのでしょうか?
iconvでエンコードの変換
以上でUTF-8対応とWindows対応は終わったのですが、Shif_JISが使えなくなったことは仕様的にデグレードではないのか?
ということで、文字コードの変換にも対応することにしました。
ソースの可用性も考えて、WindowsAPIでなくiconv使うことにしました。(もうスタティックリンクしてるし。)
そのiconvで変換がなされずに悩みました。
一つは、iconvの引数の順序が「変換元→変換先」なのに、iconv_openの引数は「変換元←変換先」であったこと。一貫性がない。
もう一つは、iconvはヌルターミネータを一切見ないこと。以下のようにして正しく文字コード変換が出来ました。
pinbuf = pstream->buff[0]; poutbuf = pstream->buff[1]; inbuf_size = strlen(pinbuf); outbuf_size = pstream->size - 1; result = iconv(pstream->ic, &pinbuf, &inbuf_size, &poutbuf, &outbuf_size ); *poutbuf = 0;
3行にて入力のヌルターミネートまで数えてあげて、6行でヌルターミネートしてあげています。
(FIFO的なプログラムを想定しているのでしょうかね?ポインタで渡すところからして。)
diffでエラー
改造した部分のパッチを作ろうと、「diff -r
」でjuman-7.10ディレクトリを比較すると「Recursive directory loop」というエラー。
その下のディレクトリを指定すると問題ありません。シンボリックリンクもまったくしていないし。(そもそもMSYSでできるのか?)
MSYS2環境自体が仮想的なパスのせいか?と思いコマンドプロントからdiffをとっても同様。
結局Windows用にビルドされた別のdiffをダウンロードして対応しました。謎。
シェルスクリプトの作成
ビルドし直したり(自分のいじったところを)デバッグしたりで何回も./configure、makeしました。
頒布のためのdiffやzipも、今後何回かは繰り返しそうです。
改造は終わってからなのですが、これらのコマンド入力をシェルスクリプトにまとめました。
この方が、ビルドの仕方が残りますし、他の人が再現できますし、何より変わった環境でのビルドの仕方をブログ記事にするとき、逐一書く必要がなくてよいですね。
今度からそうしよう。(gitのコマンドもよく忘れるから書いておこう。)
以上、ビルドから配布まででした。
まとめ
以上のように、WindowsでUTF-8対応のJUMANができました。
しかしJUMAN 7.01のリリースは2012年。
2017年になっての改造は徒労ではなかったのか?
もうMeCabでよかったのでは?
そういう疑問を残しつつも、To be continued→「Pythonモジュール化へ」