Perlでのutf-8による日本語処理 のバックアップ(No.1) - アールメカブ

アールメカブ


Perlでのutf-8による日本語処理 のバックアップ(No.1)


例えば日本語で次のようなスクリプトを用意しておくと

#!/usr/bin/perl

use utf8;
binmode(STDIN, ":utf8");
binmode(STDOUT, ":utf8");
use open ':utf8';

while(<>){
#	/(\w)/;
	/(\p{Han})/;
	print "$1\n";
}
# test.txt
これは試行です.

これで ./utf.pl < test.txt とすると,ちゃんと「試」を補足する.

以下は http://module.jp/blog/regex_unicode_prop.htmlからの引用である.

今時の正規表現ではUnicodeブロックやUnicodeスクリプトを指定して、サクッと日本語の特定文字種にマッチさせることができるのですが、毎回忘れるんです。その覚え書き。 2月売りのWEB+DB Pressの正規表現ネタ原稿がようやく終わりました。(い)さん遅くなってごめんなさいです。今回はPerlで「今時フツーの正規表現」を解説し、 Javaのjava.util.regexパッケージ、PHPのpreg系関数とereg系関数を書きまして、あと別の方に.NETの正規表現のネタを書いて頂きました。 PHP以外はいずれもUnicode対応した正規表現エンジンなので、 \p{Prop} といった形式でUnicodeにおける文字の特性(プロパティ)、言語別の書記体系(スクリプト)、文字コード上の範囲(ブロック)を指定することができます。Unicodeプロパティの簡単な例をいくつか挙げると \p{L} 表示可能な文字 \p{M} 他の文字と組み合わせて使用する文字 \p{Z} 区切り文字 \p{S} 記号 \p{N} 数字 \p{P} 句読点 \p{C} L,M,Z,S,N,P以外のすべての文字 \p{Ll} 小文字 \p{Lu} 大文字 \p{Lo} ヘブライ語や日本語などの大文字と小文字を持たない文字。 \p{Sc} 通貨記号 とか。Unicodeスクリプトの場合日本語に絡んでくるのは \p{Katakana} カタカナ \p{Hiragana} ひらがな \p{Han} 漢字 \p{Latin} 英数字 \p{Common} スペースや句読点など など。ただしUnicodeスクリプトまでサポートしている正規表現エンジンはPerlのぐらい。最後にUnicodeブロックで日本語に絡んでくるのは \p{InHiragana?} ひらがな \p{InKatakana?} カタカナ \p{InHalfwidthAndFullwidthForms?} 半角カタカナ \p{InCJKUnifiedIdeographs?} 漢字 といった感じ。ちなみにjava.util.regexとPerlのUnicodeブロックは接頭子Inを使うが、.NETの場合は接頭子Isを使う、という差異があります。 Unicodeスクリプトとブロックの違いがビミョーに見えるけど、ブロックがコードブロックをゴリッと指定したものに対して、スクリプトは特定言語に関係する文字の種類を直接指定するものなのでブロックよりも断定的、って感じで見れば良かなと。ちなみにUnicode関連のドキュメントによると Unicodeプロパティとスクリプトで日本語の文章を表そうとすると m/(?:(?:\p{Hiragana}|\p{Katakana}|\p{Han}|\p{Latin}|\p{Common})

 (?:\p{Inherited}|\p{Me}|\p{Mn})?)+/x;

こんな感じになるそうな。実際流通している文章はこれより多様なので現実とは微妙に乖離していますがだいたいこんな感じではあります。 これでなにが嬉しいかというと、文字種ごとにモリモリ文字列を切り分けようとする場合には単純に import java.util.regex.*; public class WordSplit? {

   public static void main(String[] args) {
       String text = "漢字カナ混じりの文章をinputします";
       Pattern p = Pattern.compile(
           "(\\p{InBasicLatin}+|" +
           " \\p{InHiragana}+|" +
           " \\p{InKatakana}+|" +
           " \\p{InCJKUnifiedIdeographs}+)", Pattern.COMMENTS);
       Matcher m = p.matcher(text);
   
       while (m.find()) {
           String chunk = m.group(1);
           System.out.println(chunk);
       }
   }

} とか

#!/usr/bin/perl use Encode qw/encode decode/; use strict; my $text = decode('EUC-JP', '漢字カナ混じりのビミョーな文章をinputします'); my $pat = qr/(

   \p{InBasicLatin}+                 |
   \p{InHalfwidthAndFullwidthForms}+ |
   \p{Hiragana}+                     |
   \p{Katakana}+                     |
   \p{Han}+

)/x; while ($text =~ /$pat/g) {

   print encode('EUC-JP', $1), "\n";

} __END__ とか書くだけで済んだりします。ちなみにこれらを実行すると 漢字 カナ 混 じりの ビミョー な 文章 を input します という出力を得ることができます。 で、問題はこれらをサポートしていないPHPで、以前のPerlのように使用するエンコーディングにあわせて文字種ごとのコードブロックを指定してあげる必要があります。だいたいこんな感じ。

?php $text = '漢字カナ混じりの文章をinputします'; // EUC-JP $pattern = '/(

   [\x21-\x7E]+                 | # Latin-1
  #(?:\x8E[\xA6-\xDF])+         | # HankakuKatakana
   (?:\xA4[\xA1-\xF3])+         | # Hiragana
   (?:\xA5[\xA1-\xF6])+         | # Katakana
   (?:[\xB0-\xF4][\x00-\xFF])+    # Kanji

)/x';

if (preg_match_all($pattern, $text, $capture)) {

   echo join("\n", $capture[1]), "\n";

} ?> PHPはApache 2でも利用されているPCREライブラリのおかげ、java.util.regexは中の人のおかげで、かなりの部分で最近のPerlライクな正規表現が利用できます。なので大崎さんのWebサイトにあるようなコユ〜イネタも比較的簡単に利用できるのでもしご存じない方がいらっしゃいましたら是非参照してみることをお勧めしますです。