Shift_JISでの文字列サーチする方法

CGIテクニック集トップへ





概要

Shift_JISの日本語データから文字列を検索します。UTF-8やEUC-JPに文字列やスクリプトを変更することなく検索が行えます。メッセージボード5ディスカッションで使われています。

必用な処理

  1. 検索文字列をアンパック(unpack)する
  2. 検索文字列内で正規表現の予約語に当てはまる文字コードをエスケープする
  3. エスケープ後の文字列を日本語に再パック(pack)する
  4. 検索される文字列とエスケープ処理された検索文字列をパターンマッチする処理する

サンプルコード

 # 検索する文字列
 my $search_word = 'パターン';
 my $search_word_org = $search_word;
 
 # 検索される文字列
 my $string = '検索される文字列とエスケープ処理された検索文字列をパターンマッチする処理する';
 
 $search_word =~ s/([\W])/sprintf("%%%02X", ord($1))/eg;
 
 $search_word =~ s/%5[BCDE]/%5c$&/gi;
 $search_word =~ s/%2[489B]/%5c$&/gi;
 $search_word =~ s/%3F/%5c$&/gi;
 $search_word =~ s/%7[BCD]/%5c$&/gi;
 $search_word =~ s/[\.\*]/%5c$&/g;
 
 $search_word =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
 
 my $hit;
 ("$string" =~ /$search_word/) && ($hit = 1);
 
 if ($hit) {
   print "「$search_word_org」が見つかりました。";
 } else {
   print "「$search_word_org」は見つかりませんでした。";
 }

コードの解説

 my $search_word = 'パターン';
 my $search_word_org = $search_word;

検索する文字列を変数に入れます。$search_wordは後にエスケープ処理される物で結果表示のためにエスケープされる前の文字列を$search_word_orgとして保存します。

 my $string = '検索される文字列とエスケープ処理された検索文字列をパターンマッチする処理する';

検索される文字列も変数に入れておきます。

 $search_word =~ s/([\W])/sprintf("%%%02X", ord($1))/eg;

検索文字列をアンパック(デコード)します。この時点で「パターン」という文字列は、

 %83p%83%5E%81%5B%83%93

のようになります。

 $search_word =~ s/%5[BCDE]/%5c$&/gi;
 $search_word =~ s/%2[489B]/%5c$&/gi;
 $search_word =~ s/%3F/%5c$&/gi;
 $search_word =~ s/%7[BCD]/%5c$&/gi;
 $search_word =~ s/[\.\*]/%5c$&/g;

正規表現のスペシャルな文字に関してエスケープします。半角\のコードをその文字の前に追加します。以下のような文字がエスケープされます。ここに示しているのは一例で全てではありません。

 \=%5C, (=%28, )=%29, [=%5B, ]=%5D, |=%7C
 ?=%3F, +=%2B, ^=%5E, $=%24, {=%7B, }=%7D

Shift_JISの文字では以下のような物がこれに当てはまります。ここに示しているのは一例で全てではありません。

 圭=%8C%5C、表=%95%5C、ー=%81%5B、望=%96%5D、
 評=%95%5D、従=%8F%5D、転=%93%5D、余=%97%5D、ゾ=%83%5D、犠=%8B%5D、

エスケープ語のデコードされた「パターン」という文字列は、

 %83p%83%5c%5E%81%5c%5B%83%93

のようになります。

 $search_word =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;

エスケープされた文字列を再度パック(エンコード)して日本語文字列に変換します。この時点で「パターン」という文字列は、

 パソ^―[ン

のようになります。この時点で文字がどのように見えるかは問題ではありません。半角\が入った状態で再度エンコードしているので文字が崩れて見えます。

 my $hit;
 ("$string" =~ /$search_word/) && ($hit = 1);

エスケープされた検索文字列が検索される文字列内に含まれているかチェックします。もしマッチすれば$hitが1になります。

 if ($hit) {
   print "「$search_word_org」が見つかりました。";
 } else {
   print "「$search_word_org」は見つかりませんでした。";
 }

$hitの状態により検索文字があったかどうかを表示します。予め保存しておいたエスケープ前の文字列$search_word_orgは、ここで使用されます。

関連するCGIテクニック

文字コード変換の仕方
文字コードの取得
メールのタイトル用にエンコードする方法
Perl 5.6.1でのUTF-8の処理の方法
英文スパムなどのアスキーコードのみの文字列を排除する方法