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