萩萩日記

世界に5人くらい存在するかもしれない僕のファンとドッペルゲンガーに送る日記

絵文字の正規表現が間違ってるという話

4月29日に書いた日記の絵文字の正規表現が間違ってるという指摘を、

http://miau.s9.xrea.com/blog/index.php?itemid=678

というブログで受けた。

ん?この正規表現間違ってるな。
/\G(A*?)(?:B)|(?:C)/ みたいな構造になってるから、C が文字列先頭か B の直後に現れないとうまく除去できないのでは。
/\G(A*?)(?:B|C)/ みたいな構造にしないと。

やっぱりこうやって公開してると、間違ってたら誰か賢い人が指摘してくれるから良いやね。

で、簡単なチェックプログラム書いて試してみたところ、確かに、うまく除去できてなかった。

#!/usr/local/bin/perl

use strict;
use CGI;

my $cgi = CGI->new;

my $char = $cgi->param('char');

my $SJIS_CHARS = '(?:[\x00-\x7F\xA1-\xDF]|(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]))';

$char =~ s/\G(${SJIS_CHARS}*?)(?:\xF8[\x9F-\xFC]|\xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0])|(?:\xF9[\xB1-\xFC])/$1/g;

$char = $cgi->escapeHTML($char);

print $cgi->header('text/html;charset=Shift_JIS');
print <<_;
<HTML>
<BODY>
<FORM ACTION="${ENV{SCRIPT_NAME}}" METHOD="POST">
<INPUT TYPE="TEXT" NAME="char" VALUE="${char}">
<INPUT TYPE="SUBMIT">
</FORM>
</BODY>
</HTML>
_

このCGIに「あαか○さαα○たな」と入力すると、見事に拡張絵文字が除去できてない。あらまあ。

ちなみにαが、

で拡張絵文字、○が、

で、基本絵文字。

上記ブログで指摘されているように、

$char =~ s/\G(${SJIS_CHARS}*?)(?:\xF8[\x9F-\xFC]|\xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0])|(?:\xF9[\xB1-\xFC])/$1/g;

を、

$char =~ s/\G(${SJIS_CHARS}*?)(?:\xF8[\x9F-\xFC]|\xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0]|\xF9[\xB1-\xFC])/$1/g;

にすると良いみたい。一応xを付けたのも書くと、

$char =~ s/                                                                                                            
    \G                                                                                                                       
    (
        ${SJIS_CHARS}*?
    )
    (?:
        \xF8[\x9F-\xFC]
        |
        \xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0]
    )
    |
    (?:\xF9[\xB1-\xFC])
/$1/gx;

が間違いで、

$char =~ s/
    \G
    (
        ${SJIS_CHARS}*?
    )
    (?:
        \xF8[\x9F-\xFC]
        |
        \xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0]
        |
        \xF9[\xB1-\xFC]
    )
/$1/gx;

だと正しく動くと。

ただ、

C が文字列先頭か B の直後に現れないとうまく除去できないのでは。

ってことではなくて、B(基本絵文字)とC(拡張絵文字)が混在してるとCが除去できないみたいでした。ただ、CがBの直後に現れる場合は除去できました。

きっとこれって、要するに、演算子の優先順位の問題ってことですかね。

print 'ABC' =~ /AB|CD/ ? "o\n" : "x\n";

はoになるけど、

print 'ACB' =~ /AB|CD/ ? "o\n" : "x\n";

はxになると。なんでかってーと、「A、それから次にBかC、そんでD」じゃなくて、「AB、もしくはCD」にマッチするから。だから、

print 'ACD' =~ /AB|CD/ ? "o\n" : "x\n";

とか、

print 'CAB' =~ /AB|CD/ ? "o\n" : "x\n";

はoになると。

お勉強になりました!