辞書でピンインを求める

直接DITAと関係はないのですが、中国語簡体字の文字列比較の話を書きましたので、実際そこで書いた

1. 文字列が与えられる.
2. これを単語に分けて辞書を引き、正確なピンインを求める.
3. 単語の各文字単位に「ピンイン(拼音)/画数/部首/GB0のコード」を求める.
4. このキーにより2つの文字列を比較する.

という手順の2.の部分が実現できないか試してみました.Kimberさんのコードを見るとICUのBreakIteratorを使用しているようなのですが、Saxonのクラスと深く結びついているコードなのでそのままでは手出しができません.ICUのユーザーズガイドにあったサンプルで簡単なものを試してみました.以下のようなものですが、2.のとおり文字列を与えて単語に分割して結果を出力するという簡単なものです.

import com.ibm.icu.text.BreakIterator;
import java.util.Locale;

public class StringSplitTest {

public static void main(String[] args) {
//重商主义 zhong4 shang1 zhu3 yi4
//重修旧好 chong2 xiu1 jiu4 hao3
String stringToExamine = "重商主义和重修旧好"; 
//print each word in order
BreakIterator boundary = BreakIterator.getWordInstance(Locale.SIMPLIFIED_CHINESE);
boundary.setText(stringToExamine);
printEachForward(boundary, stringToExamine);
}
public static void printEachForward(BreakIterator boundary, String source) {
    int start = boundary.first();
    for (int end = boundary.next();
         end != BreakIterator.DONE;
         start = end, end = boundary.next()) {
         System.out.println(source.substring(start,end));
    }
}
}

「重商主义」は「重商主義」で「重」は「zhong4」です.「重修旧好」は「以前の交友関係を修復する」で「重修」が「改修する」の熟語で「重」は「chong2」です.これを「和」(and)でつなげています.(中国語としては意味はありません)実行すると次のような結果となります.

重商主义
重修旧好

イメージ 1


よさそうです.これで辞書を引けば「重商主义」からは「zhong4 shang1 zhu3 yi4」、「重修旧好」からは「chong2 xiu1 jiu4 hao3」が得られ、2つの「重」は適切なピンインを当てはめることができます.実際Kimberさんの使っている辞書で見るとこの2つの単語は中にあります.

CC-CEDICT

ところでこのような単語の分割はICUの中でどのように行われているのでしょうか?それも間違いなく辞書ベースで行われているはずです.ICUのソースパッケージを落としても見当たらなかったのですが、次のURLを見るとICUが使用している辞書があります.この辞書には日本語や中国語混在で約31万語が登録されています.


ところがこのcjkdict.txtのライセンスを見ると次のような一文があります.

 # Note: This data file (cjdict.txt) was originally developed by Chromium project.
 # The original work contains words taken from CC-CEDICT distributed under CC-SA
 # license. However, CC-SA license is not compatible with ICU's MIT/X style license,
 # all of CC-CEDICT unique words were removed from the data.

ICUでも上記のCC-CEDICTを当初は使っていたようなのですが、ライセンスが合わないのでその単語は除外されたということです.ということは逆にCC-CEDICTにある単語でcjkdict.txtにないものだと単語分割はうまくゆかないということになります.以下はその一例です.

String stringToExamine = "重形式轻内容"; //zhong4 xing2 shi4 qing1 nei4 rong2 (heavy on form, light on substance/to stress form at the expense of content)

で実行すると

形式
内容

と分割されてしまい、「重」が1文字で切り出されてしまいます.「重」は形容詞のように使われているのでまあ何にでもかかることができるのかもしれません.しかし1文字で切り出されると、プログラムは「zhong4」か「chong2」かの識別はつかなくなってしまいます.やむなく多く使われている方か最初に出現したピンインを選択せざるを得ないでしょう.「重形式轻内容」な単語が索引や用語として使用されるかどうかはまったくわかりません.しかしCC-CEDICTも約10万語あります.やはりcjdictに存在しないものはそれなりにあるのかもしれません.

辞書ベースの方法でも必ずしもピンインは正確に求めることはむつかしそうです.もしcjdictに存在しないものがあって困った場合、その単語を外部から例外データとしてユーザーが簡単に登録できる機構があれば実用になるかもしれません.

これができる可能性があるのは、辞書に固定されるBreakIteratorクラスではなく、RuleBasedBreakIteratorクラスです.このコンストラクタには"ルール"を文字列として渡せます.

Class RuleBasedBreakIterator

Constructors
Constructor and Description
RuleBasedBreakIterator(String rules)
Construct a RuleBasedBreakIterator from a set of rules supplied as a string.

ところがこのrulesの説明が超難解でとても理解できそうにありません.

RBBI Rules

なんとか出来るように勉強したいと考えています.これと同じように単語自体を分割する各国語のハイフネーションも例外辞書により実用的になっているのですから.