Control Structure.

●制御構造(1998/8/20)

骨組みは組みあがったと思うので、実際にHP上で使えるようにしていきましょう。まずはhttpdに渡すために必要なヘッダの追加、HTML形式での出力、画像ファイルの利用です。ここではperlで使うことの出来る様々な機能を少しずつ利用していきます。ようやく少し言語らしい話題に入っていきます。最終的に仕上げるカウンタはcgiのみで実現し、ssiは利用しないことを念頭に置いています。さて、どんな仕様にするのが良いでしょうか。私が自分のページで使っているカウンタと同じ内容のモノをご紹介します。私が使っているカウンタは以下のような内容となっています。

1. 数字(0.gif9.gif)の画像ファイルを別途用意しておき、カウンタにはそれをimgタグで表示させる
2. カウンタのみの出力にせずに、カウンタ埋め込み済のページ全体を出力するcgiプログラムとする
3. 出力するページ全体のHTMLcgiプログラムに内蔵せずに別途HTMLファイルを用意しておき、cgiプログラムがそれを読み込んで利用する
4. カウンタイメージの埋め込みはHTML中に専用のコメントタグを入れておき、cgiプログラムがそのコメントをカウンタイメージに置き換える

こんな感じです。1は特に説明の必要はないでしょう。予め0.gif9.gifの数字の画像をバラで用意しておいて下さい。画像の大きさは全ての数字で一致している必要があります。 2ですが、これから作ろうとしているカウンタはssiを利用しないことを目的としているのでこのような仕様となっています。cgiは使えてもssiは使えないというISPがあるためです。ですからcgiプログラムを実行すればカウンタ付きのHTMLページが表示されるような作りにします。 3も自ずと理由が判るでしょう。表示させたいHTMLcgiプログラム中に直接書くことも出来ますが、そうすると普段使い慣れているHTMLエディタ(という表現で良いだろうか?)が使えなくなってしまいますよね。ですから表示したいHTMLcgiプログラムとは別ファイルにしておいて、cgiプログラムがそれを読み込んで出力するようにします。 4が重要な部分です。当然ながら読み込んだHTML中のどこかをカウンタで表示しなければならないわけですが、cgiプログラムが読み込んだHTMLのどの部分をカウンタに置き換えればよいかを予め決めておかなければなりません。ここではコメントタグをカウンタで置き換えるためのマークとして利用します。コメントタグは<!--なんとかかんとか-->という形式となっています。作ろうとしているカウンタでは<!--counter-->というコメントタグを見つけると、そこをカウンタイメージに置き換えて出力するようにします。

さて実際にプログラムを作るにあたって処理の流れを掴んでおきましょう。まずカウンタファイルを読み込んで変数に代入し、インクリメント後にまたカウンタファイルに書き戻します。次にインクリメントされたカウントを元にカウンタイメージのHTMLを作ります。最後に別に用意してあるページ全体のHTMLファイルを読み込んで、その中から<!--counter-->というコメントタグを探してもしあればそこをカウンタイメージのHTMLで置き換えます。後はそれを出力すればOKです。

カウンタファイルの読み込み、インクリメント、ファイルへの書き戻しはもういいですよね。その次の部分から説明しましょう。カウンタイメージ部分のHTMLを作成する処理についてです。早速プログラムを提示しましょう。

# カウンタ表示桁数
$entire = 6;

# カウントの桁数
$count_length = length( $count );

# カウンタ部分のHTMLを初期化
$image = '';

# 数字イメージの寸法
$width = 15; #
$height = 20; # 高さ

# カウンタイメージのHTMLを作成

# リーディング0を追加
if( $count_length < $entire )
{

$add = $entire - $count_length;
for( $i = 0; $i < $add; $i++ )
{

$image = $image . "<img src=\"0.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"0\">";

}

}

# カウントを追加
for( $i = 0; $i < $count_length; $i++ )
{

$digit = substr( $count, $i, 1 );
$image = $image . "<img src=\"$digit.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"$digit\">";

}

ようやく少しプログラムらしくなってきたでしょうか。ここでは既に変数$countインクリメント後のカウントが入っているモノとします。最初の行からいきなり知らないキーワードが出てきていますね。変数$count_lengthに何かを代入しているようです。変数名やlengthというキーワードから想像がつくかもしれませんが、ここでは変数$countに代入されている数字の長さを$count_lengthに代入しています。カウントの長さとは要するに桁数を意味します。$count5であれば1が、123であれば3が、12345であれば5$count_lengthに代入されます。lengthは指定された文字列長を表します。さて、変数$countは数値が代入されているはずです。lengthは文字列の長さを表すのでlengthには文字列を指定してやらないといけないはずですが、実際にはlength( $count )という指定で構わないのです。lengthの括弧の中身は文字列という風に明確に決まっているので、数値を指定した場合にはその数値を自動的に文字列に変えてくれます。

で、何故桁数を求めているかというと、カウンタを何桁で表示するかということがあるためです。作ろうとしているカウンタでは表示桁数を指定できるようになります。例えば6桁表示にしているときにカウントが6桁に満たない場合は高い方の位に0を補います。この0を補う処理で桁数が必要になります。0をいくつ補えばよいかを算出するためです。

次の行を見てみましょう。ここでもまた変数$imageに代入を行っていますが、ここではシングルコーテーションを2つ続けて書いています。このシングルコーテーションは実はダブルコーテーションでも同じことです。つまり文字列です。ただ文字列とはいっても中身は何もありません。空っぽの文字列を代入しています。シングルコーテーションとダブルコーテーションが同じといいましたが、本当はちょっとばかり違いがあります。以下の2つの行は全く同じ意味です。

$image = 'abc'; # 変数$imageに文字列 abc を代入
$image = "abc"; # 変数$imageに文字列 abc を代入

しかし以下の場合は違います。

$value = 123;
$image = '$value'; #
変数$imageには文字列 $value が代入される
$image = "$value"; # 変数$imageには文字列 123 が代入される

どういうことかこれで多分判ったかと思います。ダブルコーテーションで括った文字列の場合だと、文字列中に変数名を入れておけばその部分は実際には変数の中身で置き換わります。上の例で言うところの変数$valueです。この部分がその中身、つまり123で置き換わります。しかし、シングルコーテーションで括った場合には文字列中に変数名を入れても置き換わりません。そのまんま文字列となります。ですからperlで文字列を使うときはダブルコーテーションとシングルコーテーションを適宜使い分けるようにします。

$imageには最終的にカウンタイメージのHTML(imgタグ)が入ります。

次に変数が2つ登場します。$width$heightです。コメントから判ると思いますが、カウンタに使用する各数字イメージ(gifファイル)の幅と高さのピクセル数です。0.gif9.gifまで全て同じ寸法を使用します。変数$width$heightをお使いになる数字イメージの寸法に合わせて適宜変更します。例では幅が15ピクセル、高さが20ピクセルとしてあります。

次の「リーディング0を追加」というコメントの箇所に行きましょう。これが先ほど言っていた0を補う処理です。プログラムの冒頭で$entireという変数があったと思いますが、これがHTMLとして表示するカウンタの表示桁数となっています。例では6を代入しているので6桁表示です。コメント直後にifというのがあります。これが最初に説明する制御構造です。制御構造とはプログラムの処理の流れを変えるために用意されているモノのことです。ifも制御構造です。ifは条件分岐を行うための制御構造です。既に何かしらのプログラミング言語を使っておられる方はすぐに判るでしょう。お判りの方は読み飛ばして構いません。

条件分岐とはプログラムの処理の流れが枝分かれすることを言います。枝分かれするための条件を指定し、その条件を満たしているかどうかによってその後の処理の流れが変わります。具体例で説明した方が判りやすいでしょう。

if( $digit > 100 )
{

print "100を超えています\n";

}
else
{

print "100以下です\n";

}

ここでは変数$digit100を超えているかどうかを条件にしています。条件の見方は判りますよね? 数学の世界と全く同じです。条件はifの括弧の中に書きます。perlifというキーワードがあるとまず条件をチェックします。条件が満たされているとif直後の{ }で括られた部分に処理が移ります。条件が満たされていない場合にはelseというキーワードの直後の{ }で括られた部分に処理が移ります。条件でどちらかに処理が移るので、if直後の{ }内とelse直後の{ }内の両方が実行されることはあり得ません。必ずどちらかです。ifには他にもバリエーションがあります。

if( 条件 )
{

print "条件が満たされました\n";

}

if( 条件1 )
{

print "条件1が満たされました\n";

}
elsif(
条件2 )
{

print "条件1は満たされませんでしたが条件2が満たされました\n";

}

だいたい検討がつくでしょう。1つ目のバリエーションでは条件が満たされた場合の{ }しかありません。条件が満たされなかった場合には{ }内をスキップして{ }の後に処理が移ります。満たされた場合は{ }内に処理が移ってから{ }の後に移ります。2つ目のバリエーションではif直後の{ }の後に更にまた条件が入ります。その際にelseではなくelsifと書きます。elseifではないので間違えないようにしましょう。条件1がまず最初にチェックされ、それが満たされていればif直後の{ }内に処理が移ります。条件1が満たされていなければ今度は条件2がチェックされます。条件2が満たされていればelsif直後の{ }内に処理が移ります。条件2も満たされていなければelsif{ }の後に処理が移ります。例ではelsif1つだけですが、必要ならelsifはいくつでもつなげて指定できます。

さて、ifでは何を判断しているかというと0を補う必要があるかどうかです。$entireよりも$count_lengthが小さいときにif後の{ }を実行します。$entireは表示したい桁数なので6に固定です。$count_lengthは実際にカウンタファイルから読み込んでインクリメントした後のカウントの桁数です。そのインクリメント後のカウント桁数が6に満たない時に0を補います。では補う処理を見ていきます。

新しく$addという変数が登場しています。これには$entireから$count_lengthを引いています。0をいくつ補えばよいか、その個数が$addに代入されることになります。その次の行にあるforというのが2つ目の制御構造です。C/C++言語を知っている方はお馴染みかと思います。これは繰り返し処理を行うための制御構造です。forは以下のような形をとります。

for( 初期化; 条件; 毎回の処理 )
{

繰り返したい処理

}

これを見ても多分よく判らないでしょう。forの後は括弧で括り、その中には;で区切って3つの要素を指定してやります。その直後に{ }で括って繰り返し行わせたい処理を書きます。;で区切る3つの要素ですが、初期化、条件、毎回の処理となっています。forが実行されると繰り返したい処理の部分が何度も実行されるわけですが、その際に実行される順番というモノが決まっています。forの実行にあたって真っ先に実行されるのがこの初期化と書かれている部分です。次に条件がチェックされ、この条件が満たされていれば{ }内の処理を行います。{ }の処理が終わると今度は毎回の処理を実行します。これでfor1回の処理が終わります。この後また条件がチェックされ、満たされていれば{ }内を実行後に毎回の処理が実行されます。これで2回。次もまた同じように条件、{ }内、毎回の処理と続いていきます。このまま行くと永遠に繰り返し処理を続けてしまうことになりますよね。そのために条件のチェックが行われます。繰り返し処理を打ちきりたい条件をココに指定するわけです。毎回の処理の部分には、通常ですと条件に指定した内容に影響を与えるような処理を指定します。具体的に説明しましょう。

for( $i = 0; $i < 10; $i++ )
{

print "$i\n";

}

こんな例でいきましょうか。$i = 0が初期化、$i < 10が条件、$i++が毎回の処理です。まず初期化が実行されます。初期化の処理はforの実行において1回しか行われません。forの実行に先立ってただ1度実行されるだけです。ここでは変数$i0を代入しています。お気づきとは思いますが、条件、毎回の処理にも変数$iが登場します。つまり繰り返し回数のカウンタとして変数$iを使用しています。 初期化の次が条件のチェックです。変数$i10未満なら{ }内を実行します。初期化後は$i0なので当然条件が満たされて{ }内が実行されます。 { }内の処理が終わると今度は毎回の処理に移ります。ここでは$iインクリメントしています。繰り返しカウンタのカウントアップをやっています。毎回の処理はこれだけです。この後また条件のチェックになります。インクリメントされているので$i1、条件が満たされて再度{ }内が実行されます。この後ずっとこれを繰り返します。 $iが順にインクリメントされていって10になった時に、条件である$i < 10が満たされなくなります。forによる繰り返し処理は条件が満たされなくなった時点で終了します。条件のチェックを行って満たされないと繰り返し処理は終了して、for{ }の後に移ります。

さてプログラムの方に戻りましょう。ここでも繰り返し回数のカウンタとして変数$iを使用しています。条件を見ると$i < $addとなっています。変数$addには表示桁数から実際のカウント桁数を引いたモノが代入済みです。つまり変数$addは補うべき0の個数ということになり、これが繰り返し回数ということにもなります。

ではfor{ }内を見ていきましょう。変数$imageがまた登場しています。変数$imageは空っぽの文字列で初期化されています。for{ }内はこんな内容になっています。

$image = $image . "<img src=\"0.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"0\">";

変数$imageに何かを代入しているようです。後半にある文字列はもう既に判りますよね? \"が至る所に入っていますが実際には以下の内容の文字列です。

<img src="0.gif" border="0" width="$width" height="$height" alt="0">

よくあるimgタグです。カウンタの上位桁に0を追加しようとしているので0.gifというファイル名が登場するのはすぐに納得がいくでしょう。imgタグのwidthheightを見て下さい。"$width""$height"という指定があります。$が付いているので変数です。最初の方で説明した変数$width$heightです。0.gifの寸法を指定しているわけです。例では変数$width$heightにそれぞれ1520を代入しているのでwidth="15" height="20"という具合になります。

変数$imageimgタグの文字列を代入しようとしているらしいことは判ります。しかしimgタグの文字列の前にある.(ピリオド)が気になります。普通に代入するなら

$image = "<img src=\"0.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"0\">";

これでOKのはずです。どうして等号記号の後に$image .というのが付いているかというと、文字列を連結するためです。文字列の連結とは、文字通り複数の文字列をつなげて1つにすることです。つまり既に変数$imageに代入されている文字列と、ピリオドの後ろに書かれている文字列とを連結して1つの文字列とした上でまた変数$imageに代入しようとしているのです。perlはピリオドが指定されると、ピリオドの左と右に指定された文字列を連結して1つの文字列にします。例ではピリオドの左は変数$image、右はimgタグ文字列となっていますね。変数$imageに既に代入済みのimgタグ文字列に更にimgタグ文字列を追加していこうとしているわけです。これで0の追加は終わり。

次に進むとまたforが出てきます。こちらでもやってることは同じで、違いといえば0.gifimgタグをつなげるのではなくて、0.gif9.gifimgタグをつなげています。まずはforの条件を見ると繰り返しカウンタとして変数$iを使っているのは同じですが、条件が$i < $count_lengthとなっています。変数$count_lengthには最初の方でカウントの実際の桁数が代入されています。カウントの桁数分だけ繰り返し処理を行わせます。例えばカウントが123であれば3桁なのでimgタグを3回つなげることになります。さてfor{ }内を見ていると0を補うfor{ }内と同様に変数$imageimgタグ文字列を連結しているのが判ります。しかしその前に変数$digitに何やら代入を行っています。まずはここから説明を始めましょう。

変数$digitへの代入の右辺にsubstrというのがあります。このsubstrは部分文字列を取り出すモノです。部分文字列とは指定した文字列中のどこか一部分だけを取り出した文字列です。

substr( 文字列, 抽出位置, 抽出文字数 )

substrにはこのような形で指定します。括弧で括り、その中にカンマで区切って3つのモノを指定します。文字列には部分文字列の抽出対象となる文字列を指定します。抽出位置には文字列中の何文字目から抽出するかを指定します。これには0から始まる位置で指定します。つまり先頭から取り出すには02文字目から取り出すには1を指定します。抽出文字数は抽出位置から何文字取り出すかを指定します。さて、これだけの説明で変数$digitに何を代入しているか判るでしょうか? 変数$imageに連結しようとしているimgタグ文字列をちょっと見てみましょう。

$image = $image . "<img src=\"$digit.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"$digit\">";

変数$digitはこのimgタグ文字列中で使われています。使われているのはsrc=の部分とalt=の部分です。もうお判りでしょう。変数$digitには09の文字が代入されます。変数$digitに代入しているsubstrは以下のようになっています。

$digit = substr( $count, $i, 1 );

$countが文字列、$iが抽出位置、1が抽出文字数です。substrは変数$countに代入されている文字列の中から、変数$iで指定された位置(0から始まる位置)から1文字を取り出します。変数$countにはインクリメント後のカウントが代入されています。変数$iforの繰り返しカウンタで、0から順にインクリメントされていきます。つまりカウント値の上位の桁から1文字ずつ取り出しているわけです。少しsubstrの例を出しておきましょう。

$digit = substr( "ABCDEF", 3, 2 ); # 変数$digitには"DE"が代入される
$digit = substr( "GHIJKL", 0, 1 ); # 変数$digitには"G"が代入される
$digit = substr( "MNOPQR", 1, 5 ); # 変数$digitには"NOPQR"が代入される

これでお判りでしょうか? 部分文字列と言っていたのはこういう意味です。補足しておくと、substr3つ目に指定する抽出文字数は省略することが出来ます。省略した場合は抽出位置以降の全ての文字を抽出します。これも例を挙げておきます。

$digit = substr( "STUVWX", 2 ); # 変数$digitには"UVWX"が代入される

さてカウンタのimgタグ文字列はこれで完成です。ここまでの時点で一度プログラムを実行させてみましょう。cgiプログラムとしてはまだ必要な体裁が整っていないのでブラウザ上からは実行できません。直接実行させます。ここまでの段階でのプログラムを載せておきます。

#
#
適宜変更して使います
#
# $count_filename:
カウントファイル名
# $entire        :カウンタ表示桁数
# $width         :数字イメージの幅(ピクセル数)
# $height        :
数字イメージの高さ(ピクセル数)
#
$count_filename = './count.txt'; #
ファイル名
$entire = 6;                   # 桁数
$width = 15;                   #
$height = 20;                  # 高さ

# カウンタ部分のHTMLを初期化
$image = '';

# カウント読み込み、インクリメント、書き込み
open( COUNT, $count_filename );  # リードオープン
$count = <COUNT>;                 # カウント読み込み
close( COUNT );                   # クローズ
$count++;                         # インクリメント
open( COUNT, ">$count_filename" ); # ライトオープン
print COUNT "$count";             # 書き込み
close( COUNT );                   # クローズ

# カウントの実際の桁数を$count_lengthに代入
$count_length = length( $count );

# リーディング0を追加
if( $count_length < $entire )
{

$add = $entire - $count_length;
for( $i = 0; $i < $add; $i++ )
{

$image = $image . "<img src=\"0.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"0\">";

}

}

# カウントを追加
for( $i = 0; $i < $count_length; $i++ )
{

$digit = substr( $count, $i, 1 );
$image = $image . "<img src=\"$digit.gif\" border=\"0\" width=\"$width\" height=\"$height\" alt=\"$digit\">";

}

# カウンタHTMLを表示
print "$image\n";

以上の内容を例えばcounter.cgiというファイルにして、permissionを付けます。付けるべきpermissionは判りますね? またカウント値を保持するファイルも実行に先立って用意しておきます。カウント値のファイル名は任意で構いませんが、プログラムの最初の方にある変数$count_filenameの内容と一致させるようにします。例ではcount.txtとなっています。変数$count_filenameには'./count.txt'というように、./を付けています。これは今現在のディレクトリ内のcount.txtというファイルを指定しています。実際にcounter.cgiが実行される際にはcounter.cgiの置いてあるディレクトリが現在のディレクトリになります。従ってcount.txtcounter.cgiと同じディレクトリに置いておけば良いわけです。カウント値のファイルを用意したら、これにも適切なpermissionを付けておきます。準備が整ったら以下のようにして実行させてみます。

perl counter.cgi

さてどうでしょうか。カウンタのimgタグ文字列がずらずらと長いので読みにくいとは思いますが、実行する度にimgタグが正しく表示されるかどうかを確認してみて下さい。ブラウザを使用した方がHTMLの確認には良いでしょうから、実行結果をリダイレクトして適当なファイルに出力しておきましょう。そうすれば作成途中のcgiプログラムの結果であってもブラウザで見ることが出来ます。

perl counter.cgi > test.html

とりあえずここで一休みしましょうか。

このトピックで取り上げたスクリプトはココからダウンロード出来ます。圧縮してあるので適当なディレクトリに解凍してください。


戻る