MacでMeCabのJavaバインドを使用する
思ったよりも苦労したので手順をメモしておく。
MeCabとは、オープンソースの形態素解析エンジン。詳しくは MeCab Yet Another Part-of-Speech and Morphological Analyze
Macbook
Mac OS X 10.6.8
2GHz Intel Core 2 Duo
2GB 1067 MHz DDR3
$ java -version #Javaのバージョンを確認 java version "1.6.0_29" Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-10M3527) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode) $ brew --version #homebrewのバージョンを確認 0.8.1
ターミナルでMeCabを使用できるようにする。
パッケージ管理ソフトhomebrewを使って入れる。
brew install mecab mecab-ipadic #mecabとその辞書をインストールする(makeも自動で行なってくれる)
mecabコマンドを使用出来ればOK
本題のMeCabのJavaバインディング
http://code.google.com/p/mecab/downloads/list からmecab-java-0.993.tar.gzをダウンロード
tar xzvf mecab-java-0.993.tar.gz #解凍、展開する cd mecab-java-0.993
MacではこのMakefileではうまく動作してくれないので、以下のように変更する。(変更内容は、64bitアーキテクチャでコンパイルするべく、MakeFileの書き換え:http://www.cse.kyoto-su.ac.jp/~g0846020/JavaTips/mecab.htmlの通りにした。)
JAVA_BASE=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK JAVA_HOME=$(JAVA_BASE)/Home TARGET=MeCab JAVAC=javac -J-Dfile.encoding=UTF-8 JAVA=java -Dfile.encoding=UTF-8 JAR=jar -J-Dfile.encoding=UTF-8 CXX=g++ INCLUDE=$(JAVA_BASE)/Headers PACKAGE=org/chasen/mecab LIBS=-arch x86_64 `mecab-config --libs` INC=-arch x86_64 `mecab-config --cflags` -I$(INCLUDE) all: $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx $(INC) $(CXX) -dynamiclib $(TARGET)_wrap.o -o lib$(TARGET).dylib $(LIBS) $(JAVAC) $(PACKAGE)/*.java $(JAVAC) test.java $(JAR) cfv $(TARGET).jar $(PACKAGE)/*.class test: env DYLD_LIBRARY_PATH=. $(JAVA) test clean: rm -fr *.jar *.o *.dylib *.so *.class $(PACKAGE)/*.class cleanall: rm -fr $(TARGET).java *.cxxmecab-java
Makefileが正しくないとき、「java.lang.UnsatisfiedLinkError: no MeCab in java.library.path」や「Exception in thread "main" java.lang.NoClassDefFoundError: test」などのエラーが出た。
makeコマンドを実行して、libMeCab.dylibとMeCab.jarが生成されていればOK
次に、ファイルの置き場所を/usr/local/binに変更して、パスを通す
mv mecab-java-0.993 mecab-java #ファイル名を変更する mv mecab-java /usr/local/bin/ #場所を変更する cd ~/ emacs .bashrc #下記参照 source .bashrc #.bashrcの設定を有効にする
bashrcでの設定
#java encoding JAVAC='javac -encoding UTF-8 -J-Dfile.encoding=UTF-8' alias javac=$JAVAC #'javac -encoding UTF-8 -J-Dfile.encoding=UTF-8' JAVA='java -Dfile.encoding=UTF-8' alias java=$JAVA #PATHをあえて指定することで、homebrewのパスの優先度を高める。 PATH=/usr/local/bin:$PATH export PATH #java-mecab用のクラスパス #export CLASSPATH=/usr/local/bin/mecab-java:$CLASSPATH #export LD_LIBRARY_PATH=/usr/local/bin/mecab-java:$LD_LIBRARY_PATH #export DYLD_LIBRARY_PATH=/usr/local/bin/mecab-java:$DYLD_LIBRARY_PATH alias mecajavac='env DYLD_LIBRARY_PATH=".:/usr/local/bin/mecab-java" CLASSPATH=".:$CLASSPATH:/usr/local/bin/mecab-java/MeCab.jar" $JAVAC' alias mecajava='env DYLD_LIBRARY_PATH=".:/usr/local/bin/mecab-java" CLASSPATH=".:$CLASSPATH:/usr/local/bin/mecab-java/MeCab.jar" $JAVA'
これで毎回クラスパスをしていしなくても、mecajavac ◯◯.javaでコンパイル、mecajava ◯◯で実行できる。
MeCabで形態素解析のプログラムを書いてみる
import org.chasen.mecab.Tagger; import org.chasen.mecab.Node; import java.util.HashMap; import java.util.Map; import java.util.Iterator; public class MeCab { static { try { System.loadLibrary("MeCab"); //MeCabを読み込む } catch (UnsatisfiedLinkError e) { //MeCabが読み込めなかったときの処理 System.err.println("Cannot load the example native code.\nMake sure your LD_LIBRARY_PATH contains\n" + e); System.exit(1); } } public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<String, Integer>(); Tagger tagger = new Tagger ("-Ochasen");//形態素解析の準備 //String aString = "太郎は二郎にこの本を渡した。"; String aString = "吾輩は猫である。名前はまだ無い。吾輩はキャットである。名前はまだナッシン。"; System.out.println("入力: "+aString); tagger.parse(aString);//これを書いておかないと以下が上手く動作しなかった。 Node node = tagger.parseToNode(aString);//ノードに分解 for (;node != null; node = node.getNext())//ノードを順次出力 { String str = node.getFeature();//品詞等の情報を格納 String[] strAry = str.split(",");//","で区切って配列に格納 String word = node.getSurface(); //単語を格納 String feature = strAry[0]; //品詞を格納 System.out.println(word + "\t" + feature);//単語と品詞を出力 if (map.containsKey(word)){ //もし、既にmapに登録されている単語であれば、カウントをプラスする map.put(word, map.get(word)+1);//単語wordと既に格納されているカウント数に1を足して登録する }else{ //もし、まだmapに登録されていない単語であれば、カウントを1として登録する map.put(word, 1);//単語wordとカウント1をセットで格納する } } //mapから格納されている単語とカウントを出力する for (Iterator it = map.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); Object key = entry.getKey(); Object value = entry.getValue(); System.out.println("単語: " + key + "\t 出現回数: " + value); } } }
出力結果
$ mecajavac MeCab.java $ mecajava MeCab 入力: 太郎は二郎にこの本を渡した。 BOS/EOS 太郎 名詞 は 助詞 二郎 名詞 に 助詞 この 連体詞 本 名詞 を 助詞 渡し 動詞 た 助動詞 。 記号 BOS/EOS 単語: 出現回数: 2 単語: 。 出現回数: 1 単語: 二郎 出現回数: 1 単語: 本 出現回数: 1 単語: た 出現回数: 1 単語: を 出現回数: 1 単語: は 出現回数: 1 単語: 渡し 出現回数: 1 単語: 太郎 出現回数: 1 単語: この 出現回数: 1 単語: に 出現回数: 1
$ mecajavac MeCab.java $ mecajava MeCab 入力: 吾輩は猫である。名前はまだ無い。吾輩はキャットである。名前はまだナッシン。 BOS/EOS 吾輩 名詞 は 助詞 猫 名詞 で 助動詞 ある 助動詞 。 記号 名前 名詞 は 助詞 まだ 副詞 無い 形容詞 。 記号 吾輩 名詞 は 助詞 キャット 名詞 で 助動詞 ある 助動詞 。 記号 名前 名詞 は 助詞 まだ 副詞 ナッシン 名詞 。 記号 BOS/EOS 単語: 出現回数: 2 単語: まだ 出現回数: 2 単語: 。 出現回数: 4 単語: で 出現回数: 2 単語: 無い 出現回数: 1 単語: 吾輩 出現回数: 2 単語: ある 出現回数: 2 単語: 猫 出現回数: 1 単語: ナッシン 出現回数: 1 単語: は 出現回数: 4 単語: キャット 出現回数: 1 単語: 名前 出現回数: 2
参考にしたページ:MeCabをJavaで使えるようにする http://www.cse.kyoto-su.ac.jp/~g0846020/JavaTips/mecab.html
Appendix[Makefileの変更内容]
$ diff -cLINE Makefile Makefile.old *** INE --- Makefile.old 2012-02-11 01:46:45.000000000 +0900 *************** *** 1,30 **** - JAVA_BASE=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK - JAVA_HOME=$(JAVA_BASE)/Home - TARGET=MeCab ! JAVAC=javac -J-Dfile.encoding=UTF-8 ! JAVA=java -Dfile.encoding=UTF-8 ! JAR=jar -J-Dfile.encoding=UTF-8 ! CXX=g++ ! INCLUDE=$(JAVA_BASE)/Headers PACKAGE=org/chasen/mecab ! LIBS=-arch x86_64 `mecab-config --libs` ! INC=-arch x86_64 `mecab-config --cflags` -I$(INCLUDE) all: $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx $(INC) ! $(CXX) -dynamiclib $(TARGET)_wrap.o -o lib$(TARGET).dylib $(LIBS) $(JAVAC) $(PACKAGE)/*.java $(JAVAC) test.java $(JAR) cfv $(TARGET).jar $(PACKAGE)/*.class test: ! env DYLD_LIBRARY_PATH=. $(JAVA) test clean: ! rm -fr *.jar *.o *.dylib *.so *.class $(PACKAGE)/*.class ! cleanall: ! rm -fr $(TARGET).java *.cxx \ No newline at end of file --- 1,27 ---- TARGET=MeCab ! JAVAC=javac ! JAVA=java ! JAR=jar ! CXX=c++ ! INCLUDE=/usr/lib/jvm/java-6-openjdk/include PACKAGE=org/chasen/mecab ! LIBS=`mecab-config --libs` ! INC=`mecab-config --cflags` -I$(INCLUDE) -I$(INCLUDE)/linux all: $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx $(INC) ! $(CXX) -shared $(TARGET)_wrap.o -o lib$(TARGET).so $(LIBS) $(JAVAC) $(PACKAGE)/*.java $(JAVAC) test.java $(JAR) cfv $(TARGET).jar $(PACKAGE)/*.class test: ! env LD_LIBRARY_PATH=. $(JAVA) test clean: ! rm -fr *.jar *.o *.so *.class $(PACKAGE)/*.class ! cleanall: ! rm -fr $(TARGET).java *.cxx