Perl/CGIプログラムのパターンマッチ(2)

今回のPerl/CGIプログラム学習は、前回の続きでパターンマッチについて学んでいきましょう。

  1. 編集前記
  2. パターンマッチの基礎
  3. 編集後記

編集前記

今回の編集前記は、「 Perl/CGIプログラム で日本語を使うとき注意すべきこと(その1)」と題しまして、以前から前ふりしていたことについてお話していこうかと思います。

まずはその前に「なぜそもそも、Perl/CGIプログラムで日本語を使うときには注意しなくてはいけないのか?」という大きな疑問から解決しておきましょう。

それは、ものすごく乱暴に言ってしまえば、「Perl/CGIプログラム自体が、日本語を扱うようには作られていないから」この一言に集約されます。

ならどんな言語なら扱いやすいのかといいますと…。

それはお分かりのとおり、英語ですね。

英語は英語でも、半角文字や半角数字、半角記号など、日本語入力機能をオンにしなくても入力できちゃう言語が得意です。

そこに我々がむりやり?かどうかは知りませんが(苦笑)。

日本語対応モジュールやら何やらを、組み込んで使っているだけなんですね。

ベースとなるシステム自体が日本語を扱うように作られていませんから、もし日本語を扱ったものを作りたければ当然、プログラマー側で注意してやらないといけないというわけです。

と、まぁこんな感じで、大まかな理屈はわかっていただけたかと思います。

それでは具体的に、Perl/CGIプログラムで日本語を使っていくとき、どこに注意すべきかをお話していきますね。

まず、最も注意すべき点として挙げられるのが「 文字化け 」です。

文字化けというのはPerl/CGIプログラムで日本語を扱うときには、注意しておかなくてはいけない大きなポイントです。

表示させるときにはもちろん、記録したデータを扱うときにも注意しておかなくてはいけません。

そもそも「文字化けとは何ぞや?」という疑問に答えておきますと…。

文字化けとは、こちらが意図していない文字や記号が表示されてしまう現象のことです。

こいつはとにかく、ホームページが文字化けしたり、電子メールが文字化けしたり、とにかくいろいろなところで起こります。

一言で文字化けといっても種類がありまして、文書全体が文字化けしているものや、半角英数部分は問題なくて日本語部分だけが文字化けしているものなどいろいろあります。

そして、文字化けの原因にもいろいろあって…。

単純にデータが途中で切れているなど、破壊されていることが原因で起こる文字化け。

データはすべてそろっているのに、コンピューター側の文字コードの読み間違いが原因で起こる文字化け。

などなどいろいろあります。

文字化けを回避するには、まず「なぜこのような文字化けが起こっているのか?」という原因をつきとめ、それに対応したしかるべき措置をとるしかないわけですが…。

Perl/CGIプログラムで日本語を扱うときには、「文字コードの読み間違いによる文字化け」というタイプの文字化けがほとんどなので、まずはこれに注意しておけばよいでしょう。

文字コードの読み間違えとはどういうことかというと…。

日本語を表現するための文字コードというのはいくつかの種類があり、中にはPerl/CGIプログラムで扱うと、文字化けを起こしてしまう文字コードというのがあります。

そもそも文字コードというのは、「コンピューター上で文字を表現するための信号パターン」のことです。

この文字コードというのは、昔パソコンがものすごく高価な時代、会社に一台とかしかなかった時代には、同じ日本語文字でもパソコン製造メーカーごとに違っていました。

この信号パターンの場合は「あ」とか、製造メーカーが自由に決めていたわけですね。

そして時代は流れ、インターネットでいろんなタイプのコンピューターがつなげられるようになると、当然、文字コードも統一されていきました。

そして今、日本語を扱った文字コードで有名なところといえば…

UTF-8コード
EUCコード
Shift_jisコード
Jisコード

などがあります。

Jisコードに関しては、今や電子メールの本文ぐらいにしか使われていないので微妙ですが、まぁでもだいたいこの4種類に集約されます。

そしてこの中で、Perl/CGIプログラム中で使ってしまうと文字化けが発生してしまう可能性のある文字コードというのは、Shift_jisコードです。

逆に、Perl/CGIプログラムに適した日本語文字コードというのは、UTF8コード。

それに続いてEUCコードといった感じです。

これには、ちゃんとした理由があるのですが…。

今は、紙面のスペース状書けないので、希望が多ければまた別の機会に説明します。

今はとにかく、Perl/CGIプログラムで日本語を扱うときには、「Shift_jisコードの文字化けに注意」ということを覚えておいてください。

では具体的に、Shift_jisコードを使ったときに起こる文字化けというのを再現してみましょう。

あなたも機械的に「Shift_jisの文字化けには注意しろ!」と言われても、いまいち実感ないですよね。

なのでわかりやすい例えを使って、Shift_jisコードを使ったPerl/CGIプログラムで文字化けを起こしてみましょうというわけです。

#!/usr/bin/perl

print "Content-type: text/html; charset=shift_jis\n\n";
print "表示";
exit;

このPerl/CGIプログラムをShift_jisコードで保存し、サーバーにアップロードしパーミッションを割り当て、アクセスしてみてください。

見てお分かりのとおり、「表示」という文字列を出力したPerl/CGIプログラムです。

しかし、このPerl/CGIプログラムを実行しても、「表示」とは出力されず、「侮ヲ」と出力されてしまいます。

実際に動かしてみるとわかります。

あなどるの「侮」と、半角カタカナの「ヲ」が出力されます。

これが文字化けです。

こちらが意図している文字列ではなく、全然違うものが出力されてしまいますよね。

何度やり直してみても結果は同じです。

この文字化けの原因は、文字コードの読み間違えです。

具体的な読み間違えプロセスについては後述します。

それはそうと、Perl/CGIプログラムという性質上、「表示」という単語は結構使うと思うんですよね。

なのにShift_jisコードでは文字化けが起こってしまう。

例えば、「登録情報を表示」という文字列があった場合…。

Shift_jisコードで出力させると「登録情報を侮ヲ」となるわけです。

Perl/CGIプログラムでShift_jisコードを使う場合、このあたりを考慮して使っていく必要があるのです。

では、他の文字コードではどうでしょうか?

UTF-8コードとEUCコードでも、同じことをやってみましょう。

まずは、UTF-8コードバージョンからいきましょう。

#!/usr/bin/perl

print "Content-type: text/html; charset=utf-8\n\n";
print "表示";
exit;

UTF-8またはUTF-8Nコードで保存します。

後は同じようにサーバーにアップロードしパーミッションを割り当てアクセスしてみてください。

最後は、EUCコードバージョンですね。

#!/usr/bin/perl

print "Content-type: text/html; charset=euc-jp\n\n";
print "表示";
exit;

EUCコードで保存し、後は他の2つと同じです。

UTF-8コードのものもEUCコードのものも、問題なく「表示」と出力されましたよね。

もし、うまくいかなかった場合は、どこかのプロセスでミスっていますから、最初からやり直してみましょう。

実験結果はお分かりのとおり。

UTF-8コードのものとEUCコードのものは成功。

Shift_jisコードのものは文字化けなので失敗。

このような結果になりました。

ではなぜ、Shift_jisコードのものだけが文字化けを起こしてしまったのでしょうか?

結構大切なポイントですから、しっかり覚えておいてほしいのですが、いつものようにスペースがなくなってきたので、続きは編集後記で。

それではPerl/CGIプログラム学習に移っていきましょう。

パターンマッチの基礎

それでは今回も、 パターンマッチと正規表現 を組み合わせた Perl/CGIプログラム 例をいくつか紹介しますね。

範囲指定(1)

まずは、パターン文字をいくつか提示し、その中に合致するものがあるかどうかを調べるパターンマッチからいきましょう。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[PHP]/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「OK」と表示されます。

おなじみの if文による条件分岐 で、式には以前にも登場していました「[」と「]」をつかっています。

split関数 のときにもやりましたが復習をかねて、ここでしっかり学んでおきましょう。

まず、パターン文字を「[」と「]」で囲むことにより、ひとつの文字列ではなく、複数の文字としてパターン文字を提示している扱いになります。

今回の例であれば、パターンマッチ部分は「/[PHP]/」ですから、半角大文字の「P」と、同じく半角大文字の「H」が、パターン文字として扱われます。

決して「PHP」という文字列がパターン文字として指定されているわけではないことに注意しましょう。

ちなみに、2つ目の「P」については、ただ重複指定になるだけですから無駄書きですね(苦笑)。

そして文字列は「Perl/CGI」ですから、「P」がマッチしたというわけです。

範囲指定(2)

ここでも同じようにパターン文字をいくつか指定するのですが、やり方を少し変えて指定してみます。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[a-z]/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「OK」と表示されます。

今回のように「[」と「]」の中で「-」を使うと、規則的に並んでいる文字をまとめて扱うことができます。

パターンマッチ部分は「/[a-z]/」ですから、半角小文字の「a」から「z」までを、まとめて指定していることになります。

そして文字列は「Perl/CGI」ですから、「erl」がマッチしたというわけです。

今回はたまたま半角小文字の「a」から「z」まででしたが、開始位置と終了位置は自由に決めることができます。

例えば、半角小文字の「j」から「o」までの場合は、「/[j-o]/」となります。

さらに、半角大文字の場合は、「/[A-Z]/」で、半角数字の場合は、「/[0-9]/」となります。

一般的に使用されているのはこのぐらいですね。

結構便利な表現方法ですから、覚えておいてくださいね。

ちなみに、この「-」を使った範囲指定というのは、文字が定義されている順番さえわかっていれば、数字とかアルファベットといった境界を越えてまとめて指定することができます。

ではその文字が定義されている順番というのは、何が基準でそうなっているかといいますと…。

それは、 16進コード に各文字が割り当てられている順番が基準となっています。

つまり、16進数が増えていく様子を「-」を使ったパターン文字で表現すればよいわけです。

16進コードを使ったPerl/CGIプログラム作成方法についても、リクエストが多ければこの連載で解説していきます。

範囲指定(3)

今度は、逆の意味を持たせたパターンマッチです。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[^a-zA-Z\/]/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「NO」と表示されます。

「[」と「]」に囲まれた部分で「^」を使うと、「次に示す文字以外の文字が1文字でも含まれていたらパターンマッチ成立」という意味に変わります。

ちなみに、「^」が無ければ、「次に示す文字が1文字でも含まれていたらパターンマッチ成立」ですね。

今回のパターンマッチ部分は、「/[^a-zA-Z\/]/」です。

パターン文字としてあげられているものは、半角大文字と小文字のアルファベットと「/」です。

これに、「^」が付いていますから、これらのパターン文字以外の文字が含まれていたとき、例えば「@」などが含まれていたときにパターンマッチ成立となります。

比較する文字列はいつもの「Perl/CGI」ですから、パターンマッチは不成立となり、else以下が実行されたというわけです。

回数指定(1)

次は、パターン文字がくる回数(頻度)を指定したパターンマッチの例について見ていきましょう。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[a-zA-Z]*CGI/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「OK」と表示されます。

パターンマッチの中で「*」を使うと、直前のパターン文字の使用頻度を指定できます。

具体的には、「*」直前のパターン文字が0回以上マッチすることを指定しています。

ぶっちゃけた話、0回以上というのは0も含まれますから、マッチしてもしなくてもどちらでもよいことになります。

パターンマッチ部分は、「/[a-zA-Z]*CGI/」です。

つまり、「CGI」という文字列の1文字前に、半角大文字または小文字のアルファベットが来ることを示しています。

しかし、比較する文字列は「Perl/CGI」ですから、「CGI」の直前は「/」でパターンマッチで指定した文字ではありません。

普通このままでは成立しないことになりますが、「*」が付いているので、「CGI」直前に指定してある文字というのは、あってもなくてもどちらでもいいことになります。

つまり、「[a-zA-Z]というのは、あってもなくてもどちらでもOKだけど、できたら存在していてほしいな~」ぐらいの勢いしかないのです(笑)。

仮に「*」が無く「/[a-zA-Z]CGI/」この場合は、不成立となります。

ようするに、「*」が判定基準をものすごくゆるくしているわけです(笑)。

仮にアルファベットではなく数字指定のパターンマッチ「/[0-9]*CGI/」だった場合でも、「*」のおかげで成立してしまうのです。

ちなみに、「*」と似た意味を持っているものとして、「?」というものがあります。

この「*」と「?」の意味の違いがわからない人が結構いるので、ここで軽く触れておきますね。

まず、今学習しました「*」が0回以上マッチすることを指定しているのに対し、「?」は0または1回マッチすることを指定しています。

何が違うのかと言うと、「*」の場合は、とにかく0回以上であればよいのです。

つまり、1回でも2回でも8回でもいいのです。

これに対し「?」の方は、0回か多くても1回までが限度です。

2回とか8回とかはありえないわけです。

ここが大きな違いですね。

具体的なプログラム例については、パターンマッチのオプション指定を学習するときに説明しますね。

回数指定(2)

今度はもう少し、積極的なパターンマッチの例です。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[a-zA-Z]+CGI/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「NO」と表示されます。

今度は「*」ではなく「+」を使ってみました。

パターンマッチの中で「+」を使うと、直前のパターン文字に対して、1回以上マッチすることを条件にすることができます。

つまり、最低でも1回はマッチしなくてはいけないということです。

パターンマッチ部分の考え方については、前述の「*」が「+」になっただけで、そのほかは変わっていませんから、不成立になることはわかりますよね。

回数指定(3)

今度は、パターン文字の合致する回数を指定したパターンマッチです。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

$_ = "Perl/CGI";
if (/[a-zA-Z]{4}/) {
print "OK";
} else {
print "NO";
}
exit;

このPerl/CGIプログラムを実行すると「OK」と表示されます。

パターンマッチで「{」と「}」を使うと、直前に指定したパターン文字のマッチする回数を指定できます。

ただし、このマッチする回数にカウントされるのは、連続してマッチするときのみなので注意しましょう。

今回のパターンマッチ部分は「/[a-zA-Z]{4}/」です。

ということは、4回連続で、「[a-zA-Z]」がマッチするということを表しています。

比較する文字列はいつもの「Perl/CGI」です。

この中で、4文字連続している半角大文字小文字のアルファベット文字というのは、「Perl」という部分ですね。

したがって、パターンマッチが成立したというわけです。

仮に、「/[a-zA-Z]{5}/」とした場合は、パターンマッチ不成立となります。

逆に、「/[a-zA-Z]{2}/」とした場合は、パターンマッチ成立です。

ちなみに…

今回の「{n}」を、直前の文字をn回にマッチと表現するなら。

「{n,}」を、直前の文字をn回以上にマッチと表現したり。

「{n,m}」を、直前の文字をn回以上、m回以下にマッチというように表現することもできます。

まぁでもこのあたりは、あまり出てきませんから忘れちゃってもいいです。

以上今回も、パターンマッチと正規表現について学習しました。

実はまだまだあるんですが、細かくやっていたらこの連載終わらないですね。

最初は、連載3回ぐらいはかかるだろうと予想してたんですが、甘かったですね(苦笑)。

いくらなんでもこの先しばらくパターンマッチと正規表現の話ばかりしていては…。

いくら丁寧に説明したとしても、この量に圧倒されてしまって、逆に苦手意識が生まれてしまってもかわいそうなので…。

あと1回か2回ぐらいにしておきますね。

基本的なところだけでも、しっかりお伝えしておきたいですから…。

「もう終わりじゃないのかよ!」と、突っ込みを入れた人もいるかもしれませんね(苦笑)。

編集後記

今回の編集後記は、編集前記の続きです。

「表示」という文字列を出力するPerl/CGIプログラムを作成し、Shift_jis、UTF-8とEUCの3種類の文字コードで保存し実行した場合…。

なぜ、Shift_jisコードのものだけ文字化けしたのか?

という問題についてでしたね。

まぁそれは、編集前記でも書きましたように、「Shift_jisコードは文字化けを起こしやすいから」というのが答えなんですが…。

それを、具体的に説明していきましょうというのが、今回の編集後記のテーマです。

それではいきましょう。

まずは基本的なことから…。

前にも説明しましたが、Perl/CGIプログラムで使う文字や文字列というのは、16進コードでも表現できるということをやりましたよね。

頭に「\x」をつけ、その後に2桁の16進数を指定することにより、その16進コードに該当する文字を表現できるようになっています。

そして、この16進コードそれぞれにあらかじめ割りふられた文字セットというのが、UTF-8、EUCやShift_jisといった文字コードになるわけです。

例えばShift_jisコードの場合「あ」は「\x82\xA0」で表現することができます。

#!/usr/bin/perl

print "Content-type: text/html; charset=shift_jis\n\n";
print "\x82\xA0";
exit;

「あ」と表示されます。

この「\x82\xA0」という16進コードがきたときには、Shift_jisコードの「あ」を表現するということが決まっているんですね。

ですから仮に、「shift_jis」部分を、「euc-jp」や「utf-8」と書き換えた場合には、文字化けが起こってしまいます。

これは、EUCコードやUTF-8コードで「あ」という文字は、別の16進コードが割り当てられているからですね。

そしてこの16進コードの割り当てというのは、各文字コード間で重複しないような措置がとられているかというと?

実はそうでもないのです。

共通した16進数もあります。

例えば、「A」は「\x41」で表現することができます。

#!/usr/bin/perl

print "Content-type: text/html; charset=shift_jis\n\n";
print "\x41";
exit;

「A」と表示されます。

この「A」のような半角英数の場合は、他の文字コードでも同じ16進コードが割り当てられています。

つまり、「shift_jis」部分を、「euc-jp」や「utf-8」と書き換えても問題なく動作してしまうということです。

このような共通した16進コードの扱いに関しては、Perl/CGIプログラムが得意とするところです。

これが編集前記でも説明しました、日本語入力機能をオンにしなくても入力できる文字の範囲とだいたい一致しています。

つまり、Shift_jis、UTF-8やEUCなどの各文字コードの特徴の出る部分というのは、全角文字を中心とした、日本語文字に割り当てられた16進コード部分だと言えるわけです。

ここまでが基本的なところです。

次にこれらをふまえたうえで、問題の「表示」文字列を考えていきましょう。

まずは、Shift_jisコードの「表示」を表す16進コードからみていきましょう。

調べてみますと、「\x95\x5C\x8E\xA6」がそうなので…。

#!/usr/bin/perl

print "Content-type: text/html; charset=shift_jis\n\n";
print "\x95\x5C\x8E\xA6";
exit;

「表示」と表示されます。

少しおもしろいですね(笑)。

「表」が「\x95\x5C」で、「示」が「\x8E\xA6」ですね。

少し話はそれますが、これを見て、「文字化けが起こらないんだったら、16進コードで出力すればいいじゃん!」と思ったかもしれませんね。

やりたければこの方法でやってもいいですが、結構面倒ですよ。

文字化けを回避するのにもっと良い方法があるので、リクエストが多ければ別の機会に解説します。

話を戻しまして…。

「表示」に対応する16進コードについてはわかりましたね。

では次に、文字化けとして表示される「侮ヲ」についてみていきましょう。

Shift_jisコードの「侮ヲ」の16進コードを調べてみると、「\x95\x8E\xA6」であることがわかります。

#!/usr/bin/perl

print "Content-type: text/html; charset=shift_jis\n\n";
print "\x95\x8E\xA6";
exit;

「侮ヲ」と表示されます。

これら2つの16進コードを見比べれば、答えがみえてきます。

「表示」に対応した16進コード、「\x95\x5C\x8E\xA6」の中に…。

「侮ヲ」を表現するための16進コード「\x95\x8E\xA6」これらがすべて入っていますよね。

ですから単純に、文字化けの原因は、文字コードの読み間違えであることがわかると思います。

ちなみに、「侮」を表現しているのが「\x95\x8E」で、「ヲ」を表現しているのは「\xA6」です。

Shift_jisコードの場合このように、半角カタカナは日本語文字であっても1バイト文字で表現されています。

実はこれが、Shift_jisコードで文字化けが起こる原因となっているのです。

Perlパッケージプログラムが、どのようなアルゴリズムで文字コードを解釈しているのかはわかりませんが、このような文字化け現象から推理しますと…。

おそらく、単純に文字列の左側から該当する文字を探すのではなく、場所に関係なく、1バイト文字から解釈しているのではないかと推測されます。

つまり、Shift_jisコードの場合、先に、1バイトの文字である半角英数記号と、半角カタカナが解釈されます。

次に、2バイト文字である全角文字が解釈されていく感じです。

なので、「表示」を表す16進コード、「\x95\x5C\x8E\xA6」この中で最初に末尾の「\xA6」が解釈されます。

そして残りの3バイトですが、この並びに該当する文字はありませんから、前後の「\x95」と「\x8E」が解釈されたのではないかと思います。

あくまで推測ですが、悪くない推理ですよね(自画自賛)。

まぁでも本当の答えはわからないので、知っている人がいましたら、ぜひ教えてください。

みんなで共有しましょう。

ちなみに、UTF-8コードや、EUCコードでは、半角カタカナは2バイト文字で扱われていますから、このような文字化けは起こりません。

もしこのような文字化けを避けたいのであれば、UTF-8かEUCを採用するとよいでしょう。

最後に、誤解を避けるために補足説明しておきますね。

決して「Shift_jisコードは使うな!」と言ってるわけではないですからね。

日本語を使っていようといまいと、Perl/CGIプログラムの文字コードというのは、作成者の好みで自由に使いわければいいと思っています。

それに、某有名なPerl/CGI配布サイトでは、初心者向けにわざと、Shift_jisコードで作成しています。

これは、アクセシビリティーに考慮したすばらしい措置ではないかと思います。

それに、たとえShift_jisコードが文字化けを起こしやすい性質を持っていたとしても、あなたがそれを理解したうえで、文字化けを回避する手を施せば問題ないわけです。

そのような配慮ができるようになるためにも、これからも一緒にPerl/CGIプログラミングを学習していきましょう。

今回の学習は以上です。

ありがとうございました。