2009年03月14日

moshのFFIを使ってlibmecabにアクセスしてみるの巻。

FFIの使い方

(mosh ffi)をimportして、あとはこういうのとか見ながら、簡単なMeCabのバージョン表示プログラムを書いてみた:

(import (rnrs)
        (mosh)
        (mosh ffi))

(define libmecab (open-shared-library "/usr/local/lib/libmecab.1.dylib"))
(define mecab-version
  (c-function libmecab char* mecab_version))

(format #t "mecab version: ~a\n" (mecab-version))

mecab version: 0.98pre1

Good.

形態素解析してみよう

moshを開発中の22世紀の科学者に敬意を表し、「ぼく、ひげぽん。」という文を解析してみることにする。

mecab_t* は(今のところ)ポインタさえ渡せればよいので void* で代用。

(import (rnrs)
        (mosh ffi))

(define libmecab (open-shared-library "/usr/local/lib/libmecab.1.dylib"))

(define mecab-new2
  (c-function libmecab void* mecab_new2 char*))
(define mecab-sparse-tostr
  (c-function libmecab char* mecab_sparse_tostr void* char*))
(define mecab-destroy
  (c-function libmecab void mecab_destroy void*))

(define m (mecab-new2 ""))
(display (mecab-sparse-tostr m "ぼく、ひげぽん。"))
(mecab-destroy m)


|    名詞,サ変接続,*,*,*,*,*

O    名詞,一般,*,*,*,*,*

     記号,一般,*,*,*,*,*

rR    名詞,固有名詞,組織,*,*,*,*

}    名詞,サ変接続,*,*,*,*,*

?    記号,一般,*,*,*,*,*

EOS

激しく文字化けしている。内部の32ビットエンコーディングのまま渡されたようだ。

文字列をFFIに渡すためには、文字列を bytevector に変換するとよいらしい。うちのMeCabはutf-8にしてあるので string->utf8 で変換する:

(import (rnrs)
        (mosh ffi))

(define libmecab (open-shared-library "/usr/local/lib/libmecab.1.dylib"))

(define mecab-new2
  (c-function libmecab void* mecab_new2 char*))
(define mecab-sparse-tostr
  (c-function libmecab char* mecab_sparse_tostr void* char*))
(define mecab-destroy
  (c-function libmecab void mecab_destroy void*))

(define m (mecab-new2 ""))
(display (mecab-sparse-tostr m (string->utf8 "ぼく、ひげぽん。")))
(mecab-destroy m)

ぼく   名詞,代名詞,一般,*,*,*,ぼく,ボク,ボク

、    記号,読点,*,*,*,*,、,、,、

ひ    動詞,自立,*,*,一段,連用形,ひる,ヒ,ヒ

げ    名詞,接尾,一般,*,*,*,げ,ゲ,ゲ

ぽん   副詞,助詞類接続,*,*,*,*,ぽん,ポン,ポン

。    記号,句点,*,*,*,*,。,。,。

     記号,一般,*,*,*,*,*

EOS

EOSの前にゴミが入っている。

これはbytevectorがNULL終端になってないため。

文字列にNULL終端を入れるには?

安易に文字列の末尾に \0 を加え

"ぼく、ひげぽん。\0"

としてもうまく行かない。R6RS的には \0 は定義されておらず、これはエラーを出しても構わないシーケンス。

ちなみにmosh trunkはここで無限ループに陥る。

moshプロセスをkillして気を取り直したところで、R6RSの§4.2.6あたりを見て、\x0; を入れてみることにした:

(import (rnrs)
        (mosh ffi))

(define libmecab (open-shared-library "/usr/local/lib/libmecab.1.dylib"))

(define mecab-new2
  (c-function libmecab void* mecab_new2 char*))
(define mecab-sparse-tostr
  (c-function libmecab char* mecab_sparse_tostr void* char*))
(define mecab-destroy
  (c-function libmecab void mecab_destroy void*))

(define m (mecab-new2 ""))
(display (mecab-sparse-tostr m (string->utf8 "ぼく、ひげぽん。\x0;")))
(mecab-destroy m)

これでも結果は \x0; を入れる前と同じだった。調べてみると、bytevector側の末尾には 0 は付いていない。U+0000 が含まれる文字列の扱いはR6RS的にはどうすべき所なのだろうか・・・

まあいい。string->utf8したbytevectorの末尾に自分で0を付けるか:

(import (rnrs)
        (mosh ffi))

(define libmecab (open-shared-library "/usr/local/lib/libmecab.1.dylib"))

(define mecab-new2
  (c-function libmecab void* mecab_new2 char*))
(define mecab-sparse-tostr
  (c-function libmecab char* mecab_sparse_tostr void* char*))
(define mecab-destroy
  (c-function libmecab void mecab_destroy void*))

(define (string->utf8z str)
  (let* ([u8 (string->utf8 str)]
         [len (bytevector-length u8)]
         [u8z (make-bytevector (+ len 1))])
    (let loop ((i 0))
      (when (< i len)
        (bytevector-u8-set! u8z i (bytevector-u8-ref u8 i))
        (loop (+ i 1))))
    (bytevector-u8-set! u8z len 0)
    u8z))

(define m (mecab-new2 ""))
(display (mecab-sparse-tostr m (string->utf8z "ぼく、ひげぽん。")))
(mecab-destroy m)

ぼく   名詞,代名詞,一般,*,*,*,ぼく,ボク,ボク

、    記号,読点,*,*,*,*,、,、,、

ひ    動詞,自立,*,*,一段,連用形,ひる,ヒ,ヒ

げ    名詞,接尾,一般,*,*,*,げ,ゲ,ゲ

ぽん   副詞,助詞類接続,*,*,*,*,ぽん,ポン,ポン

。    記号,句点,*,*,*,*,。,。,。

EOS

これでOKです。(つづく


通信欄:test-data.scmに

(error "123\0")

を追加するといいかも。あと

(#f (string=? "123" "123\x0;456"))
(7 (string-length "ABC\x0;DEF"))

とか。>mosh開発者各位



(20:16)

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔