Perl/CGIプログラムの置換と変換

今回は、Perl/CGIプログラムで「文字または文字列を置き換える」ということを学習します。

Perlお得意のテキスト処理プログラミングの一部になります。

  1. 編集前記
  2. 置換演算子
  3. 変換演算子
  4. 編集後記

編集前記

今回の編集前記は「基数」についてお話していきます。

基数とは、数値を表現する際に各桁の重み付けの基本となる数のことです。

ものすごくばっさり言ってしまえば、数値が桁上がりする基準となる数のことです。

以前16進数や10進数について少し解説しましたよね。

ようするに今回はその続きみたいなものです。

最近この Perl/CGI講座 でも、 Perl/CGIプログラム 中に 16進数 を使って文字や数値を表現することが多くなりましたよね。

また、 パターンマッチや正規表現 もある程度学習したということで、それらをふまえ、もう少し詳しく基数についてお話していきましょうというのがここでのテーマです。

基数を学習するためには、進数という考え方から学ぶのが手っ取り早いです。

基数と進数は、切っても切れない仲ですからね。

基数という考え方を学ぶのに基本的に何進数を例に挙げてもよいのですが、有名なものとして、16進数、10進数、2進数などがありますから、これらを例に使っていきましょう。

16進数はすでにおわかりのように、Perl/CGIプログラム中でよく使用されます。

文字や数字に割り当てられている文字コードを表現できることから、少し前からこの連載でも、文字化け発生の原因や、日本語文字に対する各関数の動きを見てきました。

10進数は、われわれが普段使っている数字です。

小学生のころ算数を学びましたよね。

あの数字の動きが10進数にあたります。

2進数は、主にコンピュータの回路の動きを考えるときに使用します。

直接的に使うことはあまりないですが、プログラムを組むのであれば、この考え方は知っておいたほうがいいです。

という感じで3種類の進数を使っていくわけなのですが、これら進数にはそれぞれ独自のルールがあり、そのルールに従って値が動いていきます。

ものすごく乱暴に言ってしまえば、その値が動いていく基準となる数のことを基数といいます。

具体的には…

16進数の基数は16。

10進数の基数は10。

2進数の基数は2。

こんな感じになります。

なぜこうなるのかというと冒頭にも書きましたように、基数というのは、桁上がりが生じる数のことだからです。

これだけではわかりにくいと思うので、実際に各進数の動きを見ていきましょう。

まずはわかりやすいところからということで、10進数からいきましょう。

10進数というのは、われわれが普段使っている数字のことです。

初めは0で、1・2・3…と1つずつ数値が増えていき、9まできたら1桁目が終了します。

1桁目が終了するまでに、10種類の数を使います。

基本となる1桁を10種類の数字を使って表現することから、基数は10で10進数というわけです。

覚えづらい場合は、9の次は10つまり、1桁から2桁の値になりますよね。

この桁が上がる基準の値を基数と思っていても問題ないです。

同じように今度は16進数で考えてみましょう。

16進数も10進数の理屈と同じように、1桁の数字が16種類あります。

でも、10進数に慣れ親しみすぎたわれわれにとっては、16種類も1桁の数字だけで表現できませんよね。

ですから、途中からアルファベットを用いて不足分を補うようにします。

1桁あたりの数値は「0123456789ABCDEF」となります。

Fの次は、10となります。

1桁あたりの数値の数が増えただけで、考え方は10進数と同じですよね。

このあたりのことがわかってくると、2進数も簡単ですね。

2進数は、1桁を表現する数字の種類が0と1の2種類しかありません。

最初が0で次が1、その次が桁上がりをして10となります。

2進数はそのすべてを2種類の数値で表現することから、電気回路のオンとオフを考える上で、その動きを対応させやすいわけです。

各進数の数値の動きは理解できましたよね。

これらは、桁の上がり方は違うものの基本的な考え方は同じですから、 16進数、10進数、2進数を相互に変換する ことができます。

例えば、10進数の100は…。

16進数で表すと「64」。

2進数で表すと「1100100」。

では、16進数「FF」ではどうなると思いますか?

10進数では「255」。

2進数では「11111111」。

こんな感じになります。

数値としての大きさは変わっていませんが、基数が違うだけで雰囲気が変わってきますよね。

以上が、各進数の基本的な考え方です。

準備運動はこのぐらいにして、ここからが本番です。

いつものことながら、長い前置きでしたね(苦笑)。

ここからは、この基数という考え方を理解した上で、あなたのPerl/CGIプログラム作成にどう生かしていくかというお話をしていきます。

Perl/CGIプログラムでは、「\x」を頭につけることにより、16進数を使うことができます。

前回 前々回 では、この方法を使って文字を表現しましたよね。

例えば、このPerl/CGIプログラムを見てください。

#!/usr/bin/perl

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

「A」と表示されます。

この中の「\x」の後の「41」というのが16進数になるわけですね。

そして「\x41」が「A」なわけですから、先ほど説明しました16進数の数値の動きに従って値を増やしていけば、文字も「BCD…」という感じに変わっていきます。

まぁこれは、16進数が云々というよりも、そのようにコードが割りふられているからなのですが(苦笑)。

でも、16進数の動きを直接感じることができると思うので、ぜひ試してみてください。

例えば、こんな感じですね。

#!/usr/bin/perl

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

my @alphabet = ("\x41".."\x5A");
print @alphabet;
exit;

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

配列 「@alphabet」に16進コードで「A」から「Z」までを代入し、一度に出力させました。

「my @alphabet = ("\x41".."\x5A");」部分を解説しますと…。

まず、「..」についてはいいですよね。

以前やりましたが、「..」を使うと、一定の範囲内の値を省略することができます。

例えば、「0..9」とすると「0」から「9」までの値を指定することができます。

この仕組みを使って、ここでは「A」から「Z」までの16進コードを指定しているわけです。

でも、「A」は「\x41」だということはわかりますが、「Z」はいくつになるかわからないですよね。

「..」を使うためには、最初の値だけでなく最後の値も必要ですからね。

「Z」に使われている16進数を、以下のやり方で求めます。

まずは、「A」が「\x41」なわけですから、そこから計算すると、「Z」は「\x5A」になるのです。

アルファベットはAからZまで全部で26文字。

そしてAは16進数の「41」で、そこから残り25文字あるわけです。

10進数25を、16進数に変換すると「19」。

16進数の足し算「41 + 19 = 5A」なので、「Z」は「\x5A」になるというわけですね。

したがって、「"\x41".."\x5A"」は、「"A".."Z"」と同じ意味として扱うことができるというわけです。

16進コードで書かれていると、見た感じ難しそうですが、こうして考えていくと簡単ですよね。

この理屈がわかれば応用するのは簡単です。

次はこのPerl/CGIプログラム例を見てください。

#!/usr/bin/perl

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

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

「OK」と表示されます。

パターンマッチ部分に16進数が使われていますが…。

「\x41」が「A」。

「\x5A」が「Z」。

これがわかっていれば簡単ですね。

もし、何をやっているのかわからない場合は復習しておいてください。

次も基本となる考え方は同じですが、日本語文字向けに作成するならこんな感じになります。

#!/usr/bin/perl

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

$_ = "コーヒーと紅茶";
if (/\x82[\x9F-\xF1]/) {
print "OK";
} else {
print "NO";
}
exit;

「OK」と表示されます。

では問題です。

if文 のパターンマッチ部分に指定されている16進コードは、いったいどんな文字を表しているでしょうか?

パターンマッチ部分を書き換え、print関数で出力してみれば一瞬でわかりますが、まずは考えてみてください。

答えは後ほど発表します。

それでは今回のPerl/CGIプログラム学習に移りましょう。

置換演算子

Perl/CGIプログラム で文字列の一部を簡単に置き換える方法についてみていきましょう。

それには、 正規表現とパターンマッチ を使用した置換演算子で文字を置き換えるのが常套手段です。

置換演算子の書式

まずは、置換演算子の書き方からです。

「s/パターン文字/置換文字/」

「パターン文字」にマッチした部分を「置換文字」に置き換えます。

「パターン文字」には、パターンマッチや正規表現を使うことができます。

「置換文字」には、当然、置き換えたい文字や文字列がきます。

置換演算子の例

それでは実際に、置換演算子を使っていきましょう。

文字置換

まずは置換演算子を使って、指定した文字を置き換えてみましょう。

#!/usr/bin/perl

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

$_ = "Perl/CGI";
s/Perl/PHP/;
print;
exit;

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

パターンマッチのときと同じように、演算子のみが書かれている場合は、特殊変数「$_」内の文字列が参照されるようになっています。

「$_」内の文字列は「Perl/CGI」です。

そして、置換演算子の部分は「s/Perl/PHP/」ですから、「Perl」を「PHP」に置き換えるように指示しているというわけですね。

ちなみに、「s/perl/PHP/」のように指定した場合は、置換処理は行われません。

同じアルファベット文字でも、大文字と小文字では別の文字だと解釈されるからですね。

このあたりはパターンマッチと同じ感覚です。

それともし、特殊変数「$_」ではなく、通常のスカラ変数を使いたい場合は、「=~」を使います。

#!/usr/bin/perl

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

my $string = "Perl/CGI";
$string =~ s/Perl/PHP/;
print $string;
exit;

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

スカラ変数を左辺。

置換演算子を右辺。

その間を「=~」でつなぎます。

これもパターンマッチと同じ感覚ですね。

区切り文字の変更

置換演算子も区切り文字を別の記号に変更することができます。

ただし、split関数やパターンマッチ演算子のときのような「m」は使用しません。

#!/usr/bin/perl

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

$_ = "Perl/CGI";
s|Perl|PHP|;
print;
exit;

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

区切り文字は「|」以外にも、「@#\」などが使えます。

もちろん「/」のままでも問題ありませんが、もしパターン文字列や置換文字列の中に、「/」があった場合は直前に「\」をつけるようにします。

例えばこんな感じですね。

#!/usr/bin/perl

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

$_ = "Perl/CGI";
s/\//=/;
print;
exit;

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

区切り文字とパターン文字が同じ「/」なので、ここではパターン文字を「\/」として、区切り文字とは区別しているわけですね。

置換演算子と正規表現

置換演算子には正規表現や、16進コードなども使えます。

#!/usr/bin/perl

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

$_ = "Perl/CGI";
s/\W/\x20/;
print;
exit;

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

置換演算子の部分は「s/\W/\x20/」ですよね。

この「\W」というのは、大文字と小文字の英字、数字と「_」以外の文字を表しています。

早い話が「[^a-zA-Z0-9_]」と同じだということです。

これと似たものとして「\w」と小文字で書けば、大文字小文字の英字、数字と「_」を表すことになります。

ようするに「[a-zA-Z0-9_]」と同じ意味になるということです。

個人的感覚では、なぜ「_」まで入ってしまっているのかが、いまいち納得できないところではあるのですが(苦笑)。

まぁでも、そういうことらしいので、ここは素直にあきらめて覚えておきましょう(笑)。

話を置換演算子に戻しまして、ここでは「\W」と大文字が使われていますから、文字列「Perl/CGI」の中でマッチするのは「/」ということになるわけです。

そして次は置換文字「\x20」ですが、これは見てわかるように16進コードですね。

では何を表したものかというと、これも実行結果を見ていただければわかるように、半角スペースを表しています。

ようするに「s/[^a-zA-Z0-9_]/ /」と同じ意味だということです。

どちらが良いとか悪いとかはないです。

好みの問題ですから、どちらでも使えるようにしておきましょう。

置換演算子のオプション

次は、置換演算子の動作を設定するオプション機能についてみていきましょう。

この設定も、パターンマッチ演算子のときと同様にいろいろな種類があります。

全部紹介したいところなのですがそうするととんでもないことになりますから、いつものように有名なものだけを紹介しておきますね。

大文字小文字指定

わかりやすいところからいきましょう。

まずは、置換演算子のパターン文字に使用される、アルファベットの大文字と小文字の区別をなくす設定方法からみていきましょう。

#!/usr/bin/perl

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

$_ = "Perl/CGI";
s/perl/PHP/i;
print;
exit;

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

置換演算子の部分は、「s/perl/PHP/i」ですよね。

通常、Perlでは何も指定がない限り、アルファベットの大文字と小文字は区別して扱われます。

でも逆に、それでは不便なときもありますよね。

そんなときは、「i」を置換演算子の後ろに記述し、その区別をなくすことができます。

こうすると、「PERL」でも「PeRl」でも、パターン文字にマッチするようになります。

パターンマッチ演算子のときと同じですね。

繰り返し指定

次は、マッチした文字をすべて置換させる方法についてみていきます。

置換演算子は通常、最初にマッチしたパターン文字と置換文字を置き換えるようになっています。

例えばこれを見てください。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
s/P/p/;
print;
exit;

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

置換演算子の部分は、「s/P/p/」ですよね。

これは、大文字の「P」を小文字の「p」に変える操作を指定しています。

しかし実際には、「perl」の「p」は小文字になりましたが、「Programming」の「P」は大文字のままになっています。

これは、置換演算子のパターン文字が、最初の「P」にしかマッチしなかったことを表しています。

これでも良い場合もありますが、悪い場合もありますよね。

そんなときは以下のような指定をします。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
s/P/p/g;
print;
exit;

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

今度はすべての「P」が小文字になりましたね。

置換演算子には「g」が追加されました。

それ以外は変わっていません。

置換演算子に「g」を指定すると、パターン文字を繰り返しマッチさせることができるようになります。

これは結構頻繁に登場しますから、覚えておいたほうがいいですよ。

計算処理指定

今まではパターン文字にマッチする部分を、置換文字列に置き換えるだけでした。

ここではもう少しひねりを加え、マッチした数値に対し計算処理を施していく方法についてみていきましょう。

例えばこんな感じです。

#!/usr/bin/perl

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

$_ = "Windows 95";
s/(\d+)/$1+1905/e;
print;
exit;

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

文字列「Windows 95」から、「95」部分を取り出し、「+1905」し「2000」とし、「Windows 2000」にしています。

少々例が古いような気がしますが(苦笑)。

まぁ、気にしないでいきましょう。

置換演算子の部分は、「s/(\d+)/$1+1905/e」となっています。

置換演算子の末尾に「e」を指定することにより、パターン文字で抽出した値に対して、置換文字部分で関数や式を利用することができるようになります。

もう少し詳しくみていきましょう。

まず、「\d」というのは、数字のことです。

「[0-9]」と同じ意味を持っています。

ちなみに、「\D」のような大文字にすると、数字以外の文字を表すことになります。

「[^0-9]」と同じ意味ということです。

このような書き方もするので、覚えておきましょう。

さて、置換演算子の話に戻ります。

置換演算子のパターン文字部分は「(\d+)」となっていますよね。

これはパターンマッチですから、1文字以上の数値を指定しつつ取り出しています。

そして取り出された数値はどこに行くのかというと、特殊変数「$1」ですよね。

わからない場合は復習しておいてください。

そして置換文字部分は「$1+1905」となっていますから、「95 + 1905」という式が実行されるというわけです。

その結果、「Windows 2000」になったわけです。

複合指定

前述しました置換演算子の動作を複数指定することができるので、ここで少し紹介しておきますね。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
s/(.)/uc($1)/ge;
print;
exit;

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

マッチした1文字1文字を、uc関数を使って大文字に変換しています。

はっきり言って、「uc($_)」とすれば一度に変換できますから、ものすごく非効率な置換演算子の例ですが、このように複合指定もできるということを覚えておいてください。

変換演算子

最後に前述しました置換演算子と似たものとして、変換演算子を紹介しておきますね。

変換演算子は、文字列を1文字づつ順に調べ、対象文字列に含まれるすべての文字を変換します。

そして、変換した文字数を返します。

変換演算子の書式

「tr/変換対象の文字/変換後の文字/」

「y/変換対象の文字/変換後の文字/」

「変換対象の文字」を調べて「変換後の文字」にすべて変換します。

変換演算子の例

変換演算子の基本的な使い方をみていきましょう。

文字変換

まずは、アルファベットの小文字をすべて大文字に変換します。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
tr/a-z/A-Z/;
print;
exit;

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

変換対象の文字にアルファベットの小文字「a-z」。

変換後の文字にアルファベットの大文字「A-Z」。

このように指定するだけで、該当する文字をすべて変換してくれます。

変換演算子も特殊変数「$_」だけでなく、任意のスカラ変数を使用することができます。

#!/usr/bin/perl

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

my $string = "PerlProgramming";
$string =~ tr/a-z/A-Z/;
print $string;
exit;

実行結果は同じく「PERLPROGRAMMING」と表示されます。

左辺に文字列。

右辺に変換演算子。

その間に「=~」を記述します。

このあたりはお決まりのパターンですね。

変換数取得

変換演算子を使用して、実際に変換された文字数を取得することもできます。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
print tr/a-z/A-Z/;
exit;

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

なぜ「13」なのかと言うと、特殊変数「$_」内の文字列「PerlProgramming」の小文字の数が13だからです。

特殊変数「$_」内の文字列と、変換演算子の内容は変わっていませんから、その動作結果は前述しましたとおりです。

ここではその動作に対し、変換後の文字列ではなく、実際に変換が行われた文字数を求めています。

なので、「PerlProgramming」内の「P」はすでに大文字になっていますから…。

残りの小文字は13文字で、実際に変換が行われたのも13文字なので、「13」と表示されたというわけです。

このような例の書き方がわかりにくい場合は、特殊変数「$_」などで省略せずに書けばわかりやすくなります。

#!/usr/bin/perl

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

my $string = "PerlProgramming";
my $count = $string =~ tr/a-z/A-Z/;
print $count;
exit;

実行結果は同じく「13」と表示されます。

変換演算子のオプション

変換演算子にもその動作を制御する設定というのがいくつかあります。

例えばこれをみてください。

#!/usr/bin/perl

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

$_ = "PerlProgramming";
tr/a-z/A-Z/s;
print;
exit;

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

特殊変数「$_」と、変換演算子の内容は変わりませんが、末尾に「s」が付加されていますよね。

これは、変換後の文字列に同じ文字が連続した場合、それらをまとめて1文字にしなさいという指定です。

なので通常であれば「PERLPROGRAMMING」となるのですが、「M」が2回連続していたため、1文字に省略されてしまい「PERLPROGRAMING」になってしまったというわけです。

これで置換演算子と変換演算子の学習は以上です。

しっかり復習しておいてくださいね。

編集後記

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

#!/usr/bin/perl

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

$_ = "コーヒーと紅茶";
if (/\x82[\x9F-\xF1]/) {
print "OK";
} else {
print "NO";
}
exit;

実行結果は「OK」と表示されるのですが、どんな種類の文字に対してパターンマッチを行っているのか?

ということについて、解説していくんでしたよね。

まず、パターンマッチ部分「/\x82[\x9F-\xF1]/」というのが、2バイト文字に対するものであることはわかりますよね。

1バイト目は「\x82」。

2バイト目は「\x9F」から「\xF1」。

このあたりのことがわかれば、後は実際に出力させてみればよいだけですから簡単ですね。

「\x82\x9F」は「ぁ」。

「\x82\xF1」は「ん」。

このことから、ひらがなのパターンマッチであることがわかります。

ひらがなを表現する16進コードも、前述しましたアルファベットと同じように、ほぼ五十音順に16進コードが割り当てられています。

なのでこのようなパターンマッチの記述が可能だというわけです。

ためしに「コーヒーと紅茶」ではなく、「コーヒー&紅茶」のように、ひらがなを取り除いてみてください。

するとパターンマッチは不成立となり、「NO」が表示されるようになります。

ちなみにマッチしてしまった「と」ですが、16進コードにすると「\x82\xC6」になります。

前述しましたアルファベットにマッチするパターンマッチ。

ここで紹介しましたひらがなにマッチするパターンマッチ。

実際に使う場面はたくさんあると思うので、あなたのPerl/CGIプログラム作成にいかしてみてください。

使用上の注意としては、アルファベットのパターンマッチはほぼどの環境でも使えますが、ひらがなのパターンマッチはShift_jisコード限定ですから注意しましょう。

EUCやUTF-8コードで使用する場合は、一度Shift_jisコードに変換してから使用しましょう。

それが嫌な場合は、それぞれの文字コードに割り当てられている16進コードを調べ、新たにパターンマッチを作成するしかありません。

経験上、文字コードの変換処理をするよりも、新たにパターンマッチを作成したほうがよいです。

まぁ状況によるとは思いますが、ほとんどの場合一度変換してしまうと、戻さなくてはいけませんからね。

そのあたりを理解した上で、有効に使ってください。

今回の学習は以上です。

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