どうせ和布蕪るなら、の続き (mecabrc のあまりの使いにくさに悶絶する話)

真面目に使い始めてからずっと気になってたことである。

「システム辞書のほうをごにょってやるぜ」な向きにはあんまし気にならないんだけれど、ワタシのようにユーザ辞書だけで辞書を育てようとするノリだと、mecabrc のあまりの「機能不全」に辟易するハメになるわけだ。

ほら、これまでワタシがお披露目してきたものって、ほぼ全て「ドメイン適応のためには相応しい辞書れろ」てことで、細かくユーザ辞書作成単位を分けとるわけよ。して、「Windows なホーム問題」との併せ技でもって、仮に「HOME = USERPROFILE」として使うならば、ワタシの「.mecabrc」はこんなムゴいことになる:

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 
 3 userdic = c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/general.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/frank.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/normal_anime_game.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/adult.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_all_major.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_all_lt1000.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_all_lt100.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_major.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_lt1000.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_lt100.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_all_lt30.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_clean_lt30.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_conflict_major.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_conflict_lt1000.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_conflict_lt100.dic,  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/myoujijiten_conflict_lt30.dic,                                                                                                                                                                                                                                  c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries.dic,c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries_noun_general.dic,c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_vars_of_ipadict.dic,c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_vars_of_ipadict_noun_general.dic
 4 
 5 
 6 
 7 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_vars_of_ipadict_noun_proper.dic
 8 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries_noun_proper.dic,
 9 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_vars_of_ipadict_depends.dic
10 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries_depends.dic
11 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries_symbol.dic
12 ; c:/Users/hhsprings/AppData/Roaming/.mecab_dicts/jumandic_new_entries_symbol_emoticon.dic

途中に長い空白を挟んで「エディタでは」読みやすくなるようにしているけれど、とにかく「こうしか書けない」。なぜならば:

  1. 変数置換みたいなことが出来ない。
  2. 環境変数すら参照してくんない。
  3. 継続行の扱いすらしてない。
  4. 変数の「追加」も出来ない。

1. が出来ればきっとこうだろう:

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 mydicdir = c:/Users/hhsprings/AppData/Roaming/.mecab_dicts
 3 
 4 userdic = $(mydicdir)/general.dic,  $(mydicdir)/frank.dic,  $(mydicdir)/normal_anime_game.dic,  $(mydicdir)/adult.dic,  $(mydicdir)/myoujijiten_clean_all_major.dic,  $(mydicdir)/myoujijiten_clean_all_lt1000.dic,  $(mydicdir)/myoujijiten_clean_all_lt100.dic,  $(mydicdir)/myoujijiten_clean_major.dic,  $(mydicdir)/myoujijiten_clean_lt1000.dic,  $(mydicdir)/myoujijiten_clean_lt100.dic,  $(mydicdir)/myoujijiten_clean_all_lt30.dic,  $(mydicdir)/myoujijiten_clean_lt30.dic,  $(mydicdir)/myoujijiten_conflict_major.dic,  $(mydicdir)/myoujijiten_conflict_lt1000.dic,  $(mydicdir)/myoujijiten_conflict_lt100.dic,  $(mydicdir)/myoujijiten_conflict_lt30.dic,                                                                                                                                                                                                                                  $(mydicdir)/jumandic_new_entries.dic,$(mydicdir)/jumandic_new_entries_noun_general.dic,$(mydicdir)/jumandic_vars_of_ipadict.dic,$(mydicdir)/jumandic_vars_of_ipadict_noun_general.dic
 5 
 6 ; $(mydicdir)/jumandic_vars_of_ipadict_noun_proper.dic
 7 ; $(mydicdir)/jumandic_new_entries_noun_proper.dic,
 8 ; $(mydicdir)/jumandic_vars_of_ipadict_depends.dic
 9 ; $(mydicdir)/jumandic_new_entries_depends.dic
10 ; $(mydicdir)/jumandic_new_entries_symbol.dic
11 ; $(mydicdir)/jumandic_new_entries_symbol_emoticon.dic

これがあるだけで結構マシだ。2. も出来ればきっとこうだろう:

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 mydicdir = $(HOME)/.mecab_dicts
 3 
 4 userdic = $(mydicdir)/general.dic,  $(mydicdir)/frank.dic,  $(mydicdir)/normal_anime_game.dic,  $(mydicdir)/adult.dic,  $(mydicdir)/myoujijiten_clean_all_major.dic,  $(mydicdir)/myoujijiten_clean_all_lt1000.dic,  $(mydicdir)/myoujijiten_clean_all_lt100.dic,  $(mydicdir)/myoujijiten_clean_major.dic,  $(mydicdir)/myoujijiten_clean_lt1000.dic,  $(mydicdir)/myoujijiten_clean_lt100.dic,  $(mydicdir)/myoujijiten_clean_all_lt30.dic,  $(mydicdir)/myoujijiten_clean_lt30.dic,  $(mydicdir)/myoujijiten_conflict_major.dic,  $(mydicdir)/myoujijiten_conflict_lt1000.dic,  $(mydicdir)/myoujijiten_conflict_lt100.dic,  $(mydicdir)/myoujijiten_conflict_lt30.dic,                                                                                                                                                                                                                                  $(mydicdir)/jumandic_new_entries.dic,$(mydicdir)/jumandic_new_entries_noun_general.dic,$(mydicdir)/jumandic_vars_of_ipadict.dic,$(mydicdir)/jumandic_vars_of_ipadict_noun_general.dic
 5 
 6 ; $(mydicdir)/jumandic_vars_of_ipadict_noun_proper.dic
 7 ; $(mydicdir)/jumandic_new_entries_noun_proper.dic,
 8 ; $(mydicdir)/jumandic_vars_of_ipadict_depends.dic
 9 ; $(mydicdir)/jumandic_new_entries_depends.dic
10 ; $(mydicdir)/jumandic_new_entries_symbol.dic
11 ; $(mydicdir)/jumandic_new_entries_symbol_emoticon.dic

3. が出来ればこうだ:

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 mydicdir = $(HOME)/.mecab_dicts
 3 
 4 userdic = $(mydicdir)/general.dic, \
 5     $(mydicdir)/frank.dic, \
 6     $(mydicdir)/normal_anime_game.dic, \
 7     $(mydicdir)/adult.dic, \
 8     $(mydicdir)/myoujijiten_clean_all_major.dic, \
 9     $(mydicdir)/myoujijiten_clean_all_lt1000.dic, \
10     $(mydicdir)/myoujijiten_clean_all_lt100.dic, \
11     $(mydicdir)/myoujijiten_clean_major.dic, \
12     $(mydicdir)/myoujijiten_clean_lt1000.dic, \
13     $(mydicdir)/myoujijiten_clean_lt100.dic, \
14     $(mydicdir)/myoujijiten_clean_all_lt30.dic, \
15     $(mydicdir)/myoujijiten_clean_lt30.dic, \
16     $(mydicdir)/myoujijiten_conflict_major.dic, \
17     $(mydicdir)/myoujijiten_conflict_lt1000.dic, \
18     $(mydicdir)/myoujijiten_conflict_lt100.dic, \
19     $(mydicdir)/myoujijiten_conflict_lt30.dic, \
20     $(mydicdir)/jumandic_new_entries.dic, \
21     $(mydicdir)/jumandic_new_entries_noun_general.dic, \
22     $(mydicdir)/jumandic_vars_of_ipadict.dic, \
23     $(mydicdir)/jumandic_vars_of_ipadict_noun_general.dic
24 
25 ; $(mydicdir)/jumandic_vars_of_ipadict_noun_proper.dic
26 ; $(mydicdir)/jumandic_new_entries_noun_proper.dic,
27 ; $(mydicdir)/jumandic_vars_of_ipadict_depends.dic
28 ; $(mydicdir)/jumandic_new_entries_depends.dic
29 ; $(mydicdir)/jumandic_new_entries_symbol.dic
30 ; $(mydicdir)/jumandic_new_entries_symbol_emoticon.dic

せめてこれだけでもあったら、少なくとも「文句」にまではなってない。変数置換くらいあればいいのに、とは思うけれど、「使いにく過ぎる」とまではならん。

3. の代わりに 4. でもいい:

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 mydicdir = $(HOME)/.mecab_dicts
 3 
 4 userdic = $(mydicdir)/general.dic
 5 userdic += $(mydicdir)/frank.dic
 6 userdic += $(mydicdir)/normal_anime_game.dic
 7 userdic += $(mydicdir)/adult.dic
 8 userdic += $(mydicdir)/myoujijiten_clean_all_major.dic
 9 userdic += $(mydicdir)/myoujijiten_clean_all_lt1000.dic
10 userdic += $(mydicdir)/myoujijiten_clean_all_lt100.dic
11 userdic += $(mydicdir)/myoujijiten_clean_major.dic
12 userdic += $(mydicdir)/myoujijiten_clean_lt1000.dic
13 userdic += $(mydicdir)/myoujijiten_clean_lt100.dic
14 userdic += $(mydicdir)/myoujijiten_clean_all_lt30.dic
15 userdic += $(mydicdir)/myoujijiten_clean_lt30.dic
16 userdic += $(mydicdir)/myoujijiten_conflict_major.dic
17 userdic += $(mydicdir)/myoujijiten_conflict_lt1000.dic
18 userdic += $(mydicdir)/myoujijiten_conflict_lt100.dic
19 userdic += $(mydicdir)/myoujijiten_conflict_lt30.dic
20 userdic += $(mydicdir)/jumandic_new_entries.dic
21 userdic += $(mydicdir)/jumandic_new_entries_noun_general.dic
22 userdic += $(mydicdir)/jumandic_vars_of_ipadict.dic
23 userdic += $(mydicdir)/jumandic_vars_of_ipadict_noun_general.dic
24 
25 ; $(mydicdir)/jumandic_vars_of_ipadict_noun_proper.dic
26 ; $(mydicdir)/jumandic_new_entries_noun_proper.dic,
27 ; $(mydicdir)/jumandic_vars_of_ipadict_depends.dic
28 ; $(mydicdir)/jumandic_new_entries_depends.dic
29 ; $(mydicdir)/jumandic_new_entries_symbol.dic
30 ; $(mydicdir)/jumandic_new_entries_symbol_emoticon.dic

とにかく 1.~4. のどれかひとつだけでもあれば良かったのだが、ないわけである。ドキュメントで何も説明してなかったのだから、出来ないのは当然ではあるのだけれど、なまじね、/etc/mecabrc で「$(rcpath)」なんてのを使ってるから、出来るんでは、と期待させてしまうのが罪深い。試行錯誤で出来なかったんだよ、だからソースを見るハメになった。ほんとになんにもしてなかった。

一つ前のネタで「正直 MeCab 本体に手を入れてしまいたい気分」と言ったのは、はっきりいってこっちの方が理由。


これを「どうにかしたい」と思ったとする。はっきりいって「受け容れ、諦める」のが賢明だとも思うのだけれど、特にワタシの場合は頻繁にユーザ辞書の追加や除外をする作業真っ最中なので、非常にストレスになるんだよね、これが。


まず、「Windows の場合の少しマシ」としては、「シンボリックリンク」(Windows なので実際は junction)を使う、という手。無論「HOME = USERPROFILE」を採用するとしてね。

USERPROFILEHOME とすることにこだわらないんであれば、.mecabrc、ユーザ辞書置き場となる「HOME」を、非常に浅いところにセットすればいい。たとえば「c:/.mecabrc」「c:/.mecab_dicts」。つまり、「HOME = c:/」とする。これは滅茶苦茶最悪のアプローチと思うが、背に腹は変えられぬ、と思うなら、まぁないではない。

ただ、今どきの Windows ネィティブアプリケーションの多くが「USERPROFILE」の場所を知っている(emacs も)ことを考えると、MeCab だけがそんな場所にいても、大抵嬉しくない。emacs であれば USERPROFILE を「~」で参照してくれることもあってなおさら。

だから c:/ 直下を使うにせよ、いずれにしてもシンボリックリンクしておきたい、というわけだ。

Windows でシンボリックリンクについて、以前 MSYS の ln でちらと触れた。Windows 2000 の頃から実は使えるんだけれど、公式にはほとんど解説されてなくて、しかも「explorer すら騙される」という、危険でもあるもの。説明されないだけあって、これは C/C++ で使う場合でも DeviceIOCtl を直接叩くことでしか作れない。そうなんだけれど、SysinternalsSuite にこの junction を作るコマンドが入ってる。Unix コマンドの ln とは順序が逆なのと、パス区切りとしてスラッシュを許してくれないというのは少し使いにくいんだけれど、とにかくこのコマンドを使えば junction を作れる:

そんな場所、なので当然管理者権限で。あと MSYS から起動してることに注意。
1 [me@host: ~]$ /c/Program\ Files\ \(x86\)/SysinternalsSuite/junction.exe 'c:\.mecab_dicts' 'c:\Users\hhsprings\AppData\Roaming\.mecab_dicts'

これにより、ワタシの .mecabrc はめでたく(?):

 1 dicdir =  c:/Program Files (x86)/MeCab/dic/ipadic
 2 
 3 userdic = c:/.mecab_dicts/general.dic,  c:/.mecab_dicts/frank.dic,  c:/.mecab_dicts/normal_anime_game.dic,  c:/.mecab_dicts/adult.dic,  c:/.mecab_dicts/myoujijiten_clean_all_major.dic,  c:/.mecab_dicts/myoujijiten_clean_all_lt1000.dic,  c:/.mecab_dicts/myoujijiten_clean_all_lt100.dic,  c:/.mecab_dicts/myoujijiten_clean_major.dic,  c:/.mecab_dicts/myoujijiten_clean_lt1000.dic,  c:/.mecab_dicts/myoujijiten_clean_lt100.dic,  c:/.mecab_dicts/myoujijiten_clean_all_lt30.dic,  c:/.mecab_dicts/myoujijiten_clean_lt30.dic,  c:/.mecab_dicts/myoujijiten_conflict_major.dic,  c:/.mecab_dicts/myoujijiten_conflict_lt1000.dic,  c:/.mecab_dicts/myoujijiten_conflict_lt100.dic,  c:/.mecab_dicts/myoujijiten_conflict_lt30.dic,                                                                                                                                                                                                                                  c:/.mecab_dicts/jumandic_new_entries.dic,c:/.mecab_dicts/jumandic_new_entries_noun_general.dic,c:/.mecab_dicts/jumandic_vars_of_ipadict.dic,c:/.mecab_dicts/jumandic_vars_of_ipadict_noun_general.dic,
 4 
 5 
 6 
 7 ; c:/.mecab_dicts/jumandic_vars_of_ipadict_noun_proper.dic
 8 ; c:/.mecab_dicts/jumandic_new_entries_noun_proper.dic,
 9 ; c:/.mecab_dicts/jumandic_vars_of_ipadict_depends.dic
10 ; c:/.mecab_dicts/jumandic_new_entries_depends.dic
11 ; c:/.mecab_dicts/jumandic_new_entries_symbol.dic
12 ; c:/.mecab_dicts/jumandic_new_entries_symbol_emoticon.dic

多少圧迫感は減らせる。

「シンボリックリンクが使えるんだから Unix でも同じなんじゃね?」と思う人がいるなら、きっと Unix のことを理解出来てない。無論本来は Windows にだって言えることだけれど、「ユーザ固有のものを /home/user の外に出すなんて論外」だからね、ふつー。なので、「てめぇの MeCab 辞書」を「/」に置くなんて、まともな Unix ユーザなら絶対に考えない。Windows でも本当はこれは言えるんだけれど、結局のところは「HOME 相当の場所が深過ぎるんだよ」てことで、諦めて妥協するわけだ。


こうやってパスを短くしたところで、圧迫感こそ減らせるものの、「保守しやすさ」が劇的に改善したわけではない。

となるともう、「改変だ、改変」。

いや、だからやらないよ。

「改変」が受け容れがたい場合、「ラッパーを書く」ことは出来る。だって MeCab 本体がライブラリになってるんだからね。そのライブラリをリンクしたラッパーを書けばいい。

とはいえ、「C/C++ やだなぁめんどいし」。実際「少しマシにする」くらいならさっと書けちゃう(たぶん20分以内とかで)けれど、そんな短時間で出来るのは、上で言うせいぜい 2. か 3. のどちらか一方だけ、て程度。本当に望みのものにするためには、C/C++ のインフラ不足に祟られることで、「結構ちうかかなりめんどい」。

なので、「MeCab.py」前提の Python 前提、とするラッパーモジュールを書き、ついでに __main__ 実行可能なスクリプトにしておけば、少しは気が晴れる…であろう。

これをね、やってやろうかとも思ったんだけど、なんか今日はそういう気分じゃなくてな。割と簡単に出来るとは思うんだけれどもね。まぁ気が向いたら書いてみるわ。ひとまずワタシ的には junction で多少前進したので、今日はこれでよしとしとく。



Related Posts