prologの自然言語二分木を作るのに形態素解析はjumanでやってきた。が、前から気になっていたjuman++と比べてみたら、出力内容についてかなりの違いがあることがわかった。そこで、juman++ にしようと思ったが、jumanは自分でサーバーモードを持っていたが、juman++は、内蔵していなくて、rubyなどのラッパーで対応している。
大規模日本語コーパスの二分木づくりは、いくつものスレッドで、並列に形態素解析を行う必要がありjumanの時は、スレッドごとにjumanやknpのサーバーを立ち上げて対応した。しかし、juman++のruby経由ではうまくいかない。
そこで、juman++のjavaラッパーを作って、対応することにした。javaでjuman++を制御できれば、もともと二分木づくりはjavaでやっているので、あえてサーバーにする必要も無くなるのだが、一応、ソケット通信にも対応するようにした。
/* Jumanpp.java */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.logging.Level; import java.util.logging.Logger; public class Jumanpp { // juman++のインストールパスを指定する String jumanpath = "/usr/local/juman1.02/bin/jumanpp"; OutputStreamWriter ow; InputStream is; //InputStream es; PrintStream pis; //PrintStream pes; void getOutput(String line){ if(line.startsWith("%%CLOSE")){ System.out.println("Jumanpp: 終了します"); jumannppClose(); return; } try { ow.write(line+"\n"); ow.flush(); } catch (IOException ex) { Logger.getLogger(Jumanpp.class.getName()).log(Level.SEVERE, null, ex); } } void jumannppClose(){ try { pis.stopThread(); // 標準エラーを使う場合 //pes.stopThread(); // スレッド終了のためのダミー ow.write("terminate\n"); ow.flush(); try { Thread.sleep(1000); } catch (InterruptedException ex) { } ow.close(); is.close(); // 標準エラーを使う場合 //es.close(); } catch (IOException ex) { Logger.getLogger(Jumanpp.class.getName()).log(Level.SEVERE, null, ex); } } void jumannppStart(){ ProcessBuilder pb = new ProcessBuilder(jumanpath); System.out.println("Jumanpp: 開始します"); pb.redirectErrorStream(true); Process process; try { process = pb.start(); // is = process.getInputStream(); pis = new PrintStream(is); pis.start(); // 標準エラーを使う場合 //es = process.getErrorStream(); //pes = new PrintStream(es); //pes.start(); OutputStream os = process.getOutputStream(); ow = new OutputStreamWriter(os); } catch (IOException ex) { Logger.getLogger(Jumanpp.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String args[]) { Jumanpp jumanpp = new Jumanpp(); jumanpp.jumannppStart(); // サーバーモードで使わない場合は以下二行をコメントアウトする Server server = new Server(jumanpp,32100); server.start(); try { jumanpp.pis.join(); // 標準エラーを使う場合 //jumanpp.pes.join(); } catch (InterruptedException ex) { Logger.getLogger(Jumanpp.class.getName()).log(Level.SEVERE, null, ex); } } class PrintStream extends Thread{ BufferedReader br; boolean stop = false; PrintStream(InputStream is){ br = new BufferedReader(new InputStreamReader(is)); } void stopThread(){ stop = true; } @Override public void run(){ System.out.println("PrintStream: スレッドを開始します"); try { while(true){ String line = br.readLine(); if (line == null || stop) { break; } System.out.println(line); } br.close(); } catch (IOException ex) { Logger.getLogger(Jumanpp.class.getName()).log(Level.SEVERE, null, ex); } System.out.println("PrintStream: スレッドを終了します"); } } }
ソケット通信をする場合は、以下のクラスも使う。
/* Server.java */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; public class Server extends Thread{ int port; Jumanpp jumanpp; public Server(Jumanpp jumanpp, int port){ this.jumanpp = jumanpp; this.port = port; } @Override public void run() { try { ServerSocket ss = new ServerSocket(port); System.out.println("jumanpp サーバースタート ..."); //サーバー側ソケット作成 Socket sc = ss.accept(); String ipaddress = sc.getInetAddress().getHostAddress(); System.out.println("Connected from: " + ipaddress); juman(sc); } catch (IOException ex) { System.out.println("サーバーソケットエラー"); } System.out.println("サーバーは停止しました"); } void juman(Socket sc){ BufferedReader br; PrintWriter pw; try { br = new BufferedReader(new InputStreamReader(sc.getInputStream())); //pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sc.getOutputStream()))); String data; while((data = br.readLine()) != null){ System.out.println("受信データ:" + data); jumanpp.getOutput(data); if(data.startsWith("%%CLOSE")){ System.out.println("juman: サーバーを終了します"); break; } } //pw.println("Data received."); //pw.flush(); //pw.close(); br.close(); sc.close(); } catch (IOException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } } }