Mecab | Cabocha -I1 -f1 from Java
先日,ProcessBuilderを使ったMecabの呼び出しを書いたが,テキストの規模が大きくなるとフリーズする(プロセスがどっかへ逝く)ことが判明した.
Mecabより先にCabochaの方が限界を迎える.
原因は,JavaのBufferedStream(Reader|Writer)のバッファサイズの限界.バッファサイズを大きくしても解決出来なかった.
STDOUTとSTDERRをスレッドで読み込んで……STDINもスレッドで書き出して……とやってみたが,STDINが途中で止まってしまう.(1文STDINに出力する度にSTDOUTに解析結果が出力されることを期待したのだが)
ので,適当なデータサイズで区切って呼び出すようにしてみた.ついでに外部コマンド呼び出しをパッケージ化.
3万行のサンプルデータでの実行を確認.仮想マシン上で45秒.catとパイプを使って実行すると20秒なので,50%以上がオーバーヘッドになってしまった.
TOKEN_SIZEを150にしたところ,手持ちのデータではダメだったので,安全策を取って100に設定.
これは,テキストデータの100行分を1セットにしてMecabやCabochaを呼び出すことを意味する.
本当は,MecabとCabochaの限界は違うから別の値を設定するべきだし,バイト数で考えるべきだが,今日はここまで.
CabochaTest.java
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import org.jpn.syo.*; /** * * @author SyoTakasaki */ public class CabochaTest { /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { // INIT String file_text = args[0]; String data_text = null; String data_mecab = null; String data_cabocha = null; // テキストファイルの読み込み data_text = read_file_as_text(file_text); data_text = data_text.replaceAll("。", "。\n"); data_text = data_text.replaceAll(".", ".\n"); // Mecab data_mecab = Execute.exec("/usr/bin/mecab", data_text); // Cabocha data_cabocha = Execute.exec("/usr/bin/cabocha -f1 -I1", data_mecab); System.out.println(data_cabocha); } private static String read_file_as_text(String file_path) throws FileNotFoundException, UnsupportedEncodingException, IOException{ StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file_path), "UTF-8")); String line; while((line = br.readLine()) != null){ sb.append(line).append("\n"); } br.close(); return sb.toString(); } }
org/jpn/syo/Execute.java
package org.jpn.syo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * * @author SyoTakasaki */ public class Execute { private static final int TOKEN_SIZE = 100; public static String exec(String cmd, String inputText) throws IOException, InterruptedException { StringBuilder stdout = new StringBuilder(); StringBuilder stderr = new StringBuilder(); // command List<String> command = new ArrayList<String>(); command.addAll(Arrays.asList(cmd.split(" "))); // input text ArrayList<String> data = new ArrayList<String>(); data.addAll(Arrays.asList(inputText.split("\n"))); // mecab or cabochaの場合,適度なサイズに区切る ArrayList<String> data2 = null; if (command.get(0).endsWith("mecab") || command.get(0).endsWith("cabocha")) { data2 = new ArrayList<String>(); boolean isCabocha = false; boolean isMecab = false; if (command.get(0).endsWith("cabocha")) { isCabocha = true; } if (command.get(0).endsWith("mecab")) { isMecab = true; } int token_size = 0; StringBuilder tmp = new StringBuilder(); for (String line : data) { if ((isCabocha && line.equals("EOS")) || isMecab) { tmp.append(line).append("\n"); if (token_size > TOKEN_SIZE) { data2.add(tmp.toString()); tmp = new StringBuilder(); token_size = 0; } else { token_size++; } } else { tmp.append(line).append("\n"); } } data2.add(tmp.toString()); } else { data2 = data; } for (String d2 : data2){ Process p = new ProcessBuilder(command).start(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream())); bw.write(d2); bw.flush(); bw.close(); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while ((line = br.readLine()) != null) { stdout.append(line).append("\n"); } p.waitFor(); p.destroy(); } return stdout.toString(); } }
実行
$ java CabochaTest sample.txt