簡単な日本語文区切りプログラムの雛型


はじめに

この文書の目的は、 「日本語 (EUC-JP) の処理を jperl や最新の perl や特別 なモジュールなしの環境でも安心して行えるようにするノウハウ」 の紹介です。 まあ、なんというか、バッドノウハウですね。 「過去に jperl で書かれたプログラム遺産をどうやって perl で動かすか?」という問題も解決するかもしれません。 例として、日本語の文切りを取り上げます。

問題

EUC-JP で書かれた日本語テキストファイルを仮定して、 「。」や「.」をヒントに自動的に文を区切るプログラムを考えます。

いきなりですが、昔あった jperl というのを使えば、 以下のようなプログラムを書くだけでおしまいです。めでたし、めでたし (プログラムも EUC-JP で書かれていると仮定します)。

% cat a.pl
while (<>) {
    s/(。|.)/$1\n/g;
    print;
}
% cat a
これはペンですよ。ははは
% jperl a.pl < a    
これはペンですよ。
ははは

しかし、最近は jperl も見かけなくなってきました。 こう簡単にはいかないようです。

だからといって、日本語の文切りができる既存のモジュールなどを探して、 取って来て、インストールして、説明を読んで、... ってのは考えるだけでも面倒くさいです。 UTF-8 に変換するとか perl 5.8 をインストールするといった話も大仰です。 そこで、多くの UNIX 系の環境にすでに入っている perl だけで、 日本語の文切りを行いたいと思います。

さて、前の例と同じプログラムを jperl でなく普通の perl で実行してみましょう。 お、できてますねえ。いいじゃないすか。

% cat a.pl
while (<>) {
    s/(。|.)/$1\n/g;
    print;
}
% cat a
これはペンですよ。ははは
% perl a.pl < a    
これはペンですよ。
ははは

しかし、世の中そんなに単純ではありませんでした。 以下のような文字列を含む文は、予期せぬところで切られてしまいます (jperl では問題ありません)。お試しあれ。

〜法31により〜
〜点検ABC〜
〜◆2003:今年は〜
〜お粥3杯〜
〜教諭Aが〜

ちょっと解説してみます。 文区切り記号として使っている「。」のバイト列は 「0xa1 + 0xa3」なので、 バイト列「何か + 0xa1」と バイト列「0xa3 + 何か」の文字が連続して現れると、 文字の境界を超えて「0xa1 + 0xa3」にマッチしてしまうのです。 例えば、下図では、 「A」という 2 バイト文字のちょうど真ん中で切られてしまいます。 jperl はここらへんの文字境界をきちっと認識しているので問題ないのです。

b8a1 a3c1

解決案

というわけで、EUC として文字を 1 文字ずつ認識させながら、 文区切り記号とマッチさせるという方針を取ります。 スピードは落ちるかもしれませんが安心であります。 これなら余計な悪さもあまりしないでしょう。

#!/usr/bin/env perl
while (<>) {
    while (/([\x80-\xff]{2}|.)/g) {
        print "$1";
        print "\n" if ($1 =~ /(。|.)/);
    }
    print "\n";
}

私は普段あまり目にしませんが、EUC-JP には 3 バイトで構成される文字もあります。 というわけで、厳密に文字を認識したい場合には以下のように書き換えます。

#!/usr/bin/env perl
while (<>) {
    while (/([\xa1-\xfe]{2}|\x8e[\xa1-\xdf]|\x8f[\xa1-\xfe]{2}|.)/g) {
        print "$1";
        print "\n" if ($1 =~ /(。|.)/);
    }
    print "\n";
}

おわりに

以上、 これまでも多方面でさんざん語られてきた話題ですが、 自分なりにまとめてみました。 お役に立てば嬉しいです。 もっと簡単ですっきりした方法があれば、ぜひ教えてください。

参考文献


ツール一覧 / たつをのホームページ