Array.
配列(1998/8/22)
次にする処理は別途用意してあるHTMLファイルを読み込んで、カウンタ埋め込み用コメントを用意しておいたimgタグ文字列で置き換えるという部分です。まずはHTMLファイルの読み込みからですね。どういうプログラムにするかはもう判りますよね? カウント値のファイルを読み込んだときと同じ事をするだけです。ですが、全く同じでは学ぶところが少ないので少しだけ変えてみます。
# HTMLを読み込み
open( HTML, './top.html' );
@html = <HTML>;
close( HTML );
カウント値読み込みの時と殆ど何も変わりませんが読み込んだモノを格納する所だけ違いがあります。カウント値の時は変数$countに代入していました。今度のプログラムでは変数を表す$が付いていません。代わりに@が付いています。@が付いていても変数には違いはないのですが、@が付いている場合はそれは配列です。通常1つの変数には1つしか代入できません。当然ですね。ですが配列の場合は、1つの名前で複数のモノを代入しておけます。では配列@htmlにどのような内容が代入されるのか説明します。
<html>
<head>
<title>Title</title>
</head>
<body>
Welcome!
</body>
</html>
仮にtop.htmlが上記の内容だったとします。ここでこれを読み込むと、配列@htmlにどのように格納されるかというと
$html[ 0 ] ← <html>
$html[ 1 ] ← <head>
$html[ 2 ] ← <title>Title</title>
$html[ 3 ] ← </head>
$html[ 4 ] ← <body>
$html[ 5 ] ← Welcome!
$html[ 6 ] ← </body>
$html[ 7 ] ← </html>
こんな具合です。まずは見方から説明しましょう。左側に$html[ 0 ]〜$html[ 7 ]とあります。配列には複数のモノを代入できるといいましたが、実際には通常の変数が複数寄り集まったモノです。それら複数の変数を同じ名前で使用できるというだけです。従って配列の全体ではなく配列の特定の要素を表すときには通常の変数と同じ$が先頭に付いています。しかし同じ名前で使用できると言っても、複数あるうちどれを使用するかを指定する術がないと困るので、配列の特定要素の場合には添え字と呼ばれる数値を指定します。この添え字が[ ]内の0〜7の数値です。つまり3つ目の$htmlとか、7つ目の$htmlという指定の仕方をするわけですね。 注意が必要なのは、配列の添え字は0から始まるということです。1からではないので間違えないようにしましょう。この勘違いが原因でプログラムのミスを誘うことがよくあります。配列@htmlの要素数は8個ですが、添え字は0〜7です。添え字の最大値は配列の要素数-1となります。説明が遅くなりましたが、配列の特定の要素を指定する添え字は[ ]で括ってその中に指定します。必ず角括弧を使用します。他の括弧では代用できません。
さて左側の見方は良いでしょうか。今度は矢印の右側です。top.htmlの内容と何ら変わったところがありません。しかし、top.htmlの内容は改行されているとはいえ、実際にはひと続きの内容です。ですが配列@htmlにはそれぞれ行毎に区切られて代入されています。つまりファイルから配列に読み込んだ際に改行が現れる度にそこで区切って配列の各要素に代入されたことになります。この配列に読み込む部分をもし仮に通常の変数に読み込むとtop.htmlの一番最初の行だけが変数に代入されます。通常の変数を使うか、配列を使うか、プログラム処理上の都合等を考えて好きな方を選んで下さい。ここでは配列を使うことにします。
さてHTMLファイルの読み込みは出来たので、今度はこの中から予め決めておいたコメントタグを探して、imgタグ文字列を用いてその部分を置き換えるという処理です。探し出すという処理を行うにあたって、配列@htmlに格納されている各行毎に中身を調べに行くというプログラムにすることになります。前に説明した制御構造であるforを使ってこんな感じにすることが出来ます。
for( $i = 0; $i < 配列要素数; $i++ )
{$html[ $i ]からカウンタ置き換え用コメントタグを探す処理
}
繰り返しカウンタとして変数$iを用いています。繰り返しの打ち切り条件としては配列の要素数を指定しなければなりません。配列の要素数はどのようにして求めるのでしょうか? 実は非常に簡単でして
$lines = @html;
こうするだけです。でもちょっとよく判らないでしょう。そのまま解釈すれば通常の変数である$linesに配列@htmlを代入する、ということになります。変数に配列を代入するとはどういうことなのでしょうか。この場合には変数$linesには配列@htmlの要素数が代入されます。配列@htmlの各要素が保持している内容は一切関係ありません。要素数になるのです。配列@htmlは$html[ 0 ]〜$html[ 7 ]なので要素数は8となり、これが変数$linesに代入されます。これを用いれば以下のようにプログラムを書くことが出来ます。
$lines = @html;
for( $i = 0; $i < $lines; $i++ )
{$html[ $i ]からカウンタ置き換え用コメントタグを探す処理
}
判りますよね? しかし実はもっと便利な制御構造があります。制御構造forのバリエーションとして以下のようなモノもあります。
foreach スカラー変数 ( リスト )
{繰り返したい処理
}
難しい用語が出てきましたのでこれから説明します。スカラー(scalar)とは数値や文字列といった最も単純な値のことです。そしてスカラーを代入できる変数をスカラー変数と言います。今まで扱ってきた$countや$lines等は全てスカラー変数です。またリスト(list)とはスカラーを列挙して並べたモノです。ではスカラーって? というわけで、以下の例を見て下さい。
@values = ( 10, -8, 3, -1, 6 );
ここでは配列@valuesに何かを代入していますが、この代入の右辺にあるのがリストと呼ばれるモノです。リストは括弧で括り、その中にスカラーをカンマで区切って列挙します。さて、配列にリストを代入するとどうなるか、おそらく想像がついていることかと思います。
$values[ 0 ] ← 10
$values[ 1 ] ← -8
$values[ 2 ] ← 3
$values[ 3 ] ← -1
$values[ 4 ] ← 6
このように配列の各要素毎にリスト中のスカラーが順に代入されます。それではforeachの話に戻りましょう。制御構造foreachはリストに指定された各スカラーを順にスカラー変数に代入しながら繰り返し処理を行ってくれます。つまり繰り返し回数はリストに指定されたスカラーの個数ということになります。従って繰り返しの打ち切り条件を指定する部分はforeachにはなく、リストに指定されたスカラーの個数分だけ繰り返し処理を行った後に自動的に終了します。まだピンと来ないかもしれませんね。具体的なプログラムで見ていきましょう。
foreach $value ( 1, 2, 3, 4, 5 )
{print "繰り返し$value回目\n";
}
上記のプログラムではリストとして5つのスカラーを指定しています。foreachはどのように実行を始めるかというと、まずリストからスカラーを1つ取り出します。次に取り出したスカラーをスカラー変数(この例では変数$value)に代入して、繰り返し処理(ここではprint)を実行します。これと全く同じ事をリストに指定した全てのスカラーに対して行います。つまり変数$valueには1、2、3、4、5が順に代入されていきます。
これから行おうとしているのは配列@htmlに読み込み済みの各HTML行毎に順にコメントタグを探していくという処理です。
foreach $line ( @html )
{$lineからカウンタ置き換え用コメントタグを探す処理
}
これで各HTML行を調べていく構造を実現できます。リストとして配列@htmlを指定していますが、前に配列にリストを代入していたことを思い出して下さい。配列にリストを代入できるのですから、逆に配列からリストを取り出すことも出来るはずです。つまり配列の各要素に代入されている内容を取り出して列挙したモノがリストとなるはずです。foreachがリストを要求しているところで配列を指定したので、配列からリストが自動的に生成されます。以前、文字列を要求するlengthに数値を指定したことがありました。その時はlengthが文字列を要求しているので、自動的に数値が文字列に変換されました。foreachでも同じ事が行われているのです。配列にリストを代入でき、また配列からリストを取り出せるので配列とリストは実体は同じようなモノです。変数名を持たずに直接スカラーを列挙しているのがリストというだけです。試しに以下のプログラムを実行してみて下さい。
foreach $line ( @html )
{print $line;
}
予め予測していた通りの実行結果になると思います。さて、各HTML行毎に何かを行うための制御構造が判ったので今度はHTML行からカウンタ置き換え用のコメントタグを探し出す処理です。ここで新たにindexというモノを使ってみます。
index( 文字列, 部分文字列, 検索位置 )
indexには上記の通りカンマで区切って3つ指定します。indexは文字列中から部分文字列を探しだし、見つかればその位置を0から始まる位置で表します。見つからなければ-1となります。また検索位置は検索を開始する文字列中の位置を指定します。これも0から始まる位置で指定します。尚、検索位置は省略可能で、その場合には先頭から探します。例を見て下さい。
$position = index( "ABCDEFGHIJKL", "FGH", 0 ); # 変数$positionには5が代入される
$position = index( "ABCDEFGHIJKL", "XYZ", 0 ); # 変数$positionには-1が代入される
$position = index( "ABCDEFGHIJKL", "CDE", 6 ); # 変数$positionには-1が代入される
$position = index( "ABCDEFGHIJKL", "FGH" ); # 1つ目の例と同じ
1つ目の例ですが、文字列"ABCDEFGHIJKL"の中から部分文字列"FGH"を探します。探し始める位置は0、つまり文字列の先頭からです。部分文字列"FGH"は6文字目にあります。indexは0から始まる位置を表すので変数$positionには5が代入されます。2つ目の例では部分文字列"XYZ"を探しますが、文字列中に"XYZ"はないので変数$positionには-1が代入されます。3つ目は部分文字列"CDE"を探します。文字列"CDE"は文字列中にありますが、検索位置として6を指定しているので7文字目(Gの位置)からしか探さないので見つからずに変数$positionは-1となります。4つ目は検索位置を省略したケースです。
HTML行からコメントタグを見つけるには文字列にHTML行を、部分文字列にコメントタグ文字列を指定すれば良いですね。
$counter_comment = '<!--counter-->'; # カウンタ置き換え用コメントタグ
foreach $line ( @html )
{
# カウンタ置き換え用コメントタグを探す
$position = index( $line, $counter_comment );# 見つかったら...
if( $position != -1 )
{# カウンタのHTMLで置き換え
substr( $line, $position, length( $counter_comment ) ) = $image;}
print $line; # HTML行を出力
}
結構盛りだくさんな内容となりました。まずここではカウンタ置き換え用コメントタグとして<!--counter-->を用いています。これは変数$counter_commentに代入しておきます。ファイルから読み込んだHTMLは配列@htmlに入っているので、foreachでHTMLの各行毎に変数$lineに代入してforeachの{ }内を実行します。{ }内では変数$lineに代入されているHTML行の中から、変数$counter_commentに代入されているコメントタグ文字列を探しだし、その位置を変数$positionに代入しています。 ifの{ }内は変数$positionが-1でない場合に実行されます。この-1でなかったら、という条件の表現方法を覚えておきましょう。記号は!=です。$position != -1は変数$positionが-1と等しくなければ、という意味です。逆に変数$positionが-1と等しければ、という場合には$position == -1という風に書きます。記号は==です。
さて肝心なのはifの{ }内です。ここでカウンタ置き換え用コメントタグ部分をカウンタのimgタグ文字列で置き換えています。以下のようになっています。
substr( $line, $position, length( $counter_comment ) ) = $image;
substrは以前にも出てきました。これは部分文字列を表すモノでした。ここでは$lineで指定された文字列中の$positionで指定された位置から変数$counter_commentの長さ分の文字数を取り出します。ですが以前にsubstrが出てきた時は代入の右辺で使っていました。今回は代入の左辺にsubstrが使われています。実はsubstrは代入の左辺で使うことができ、その場合にはsubstrによって抽出された部分文字列の部分を右辺の内容で置き換えてくれます。言い方を変えると、substrによって抽出された部分文字列の部分に右辺の内容を代入します。もう少し処理内容を追いながら説明しましょう。
$line = "<center><!--counter--></center>";
$counter_comment = "<!--counter-->";
$position = index( $line, $counter_comment );
substr( $line, $position, length( $counter_comment ) ) = "<img src=...>";
indexは文字列"<center><!--counter--></center>"中から"<!--counter-->"を探します。9文字目にあるので変数$positionには8(9文字目は0から数えると8)が代入されます。substrは文字列"<center><!--counter--></center>"の$position文字目、つまり9文字目(0から数えて8だから9文字目)から"<!--counter-->"の文字数分だけ抽出します。要するに早い話が"<!--counter-->"の部分を抽出しています。その抽出した部分にimgタグ文字列を代入することで置き換えが起こります。 substrの実行結果、変数$lineの内容は"<center><img src=...></center>"となります。本当はsubstrを使わなくても正規表現と呼ばれるモノを使うともっとスマートにプログラムを書けるんですが、正規表現を使うにはまだ時期が早いと思うのでそれには触れませんでした。正規表現については別のトピックで触れるつもりでいます。
後は変数$lineをprintで出力しているだけです。これにてようやくカウンタの完成となります。残るはcgiプログラムとして守らなければならないルールを満たすだけです。今まで説明してきたことを全て盛り込んだ最終的なプログラムを載せます。
#! /usr/local/bin/perl
# どうやって実行するかを指定するコメント行#
# 適宜変更して使います
#
# $count_filename:カウンタファイル名
# $html_filename :HTMLファイル名
# $entire :カウンタ表示桁数
# $width :数字イメージの幅(ピクセル数)
# $height :数字イメージの高さ(ピクセル数)
#
$count_filename = './count.txt'; # カウンタファイル名
$html_filename = './top.html'; # HTMLファイル名
$counter_comment = '<!--counter-->'; # カウンタ置き換え用コメントタグ
$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読み込み
open( HTML, $html_filename ); # リードオープン
@html = <HTML>; # 配列に行毎に読み込み
close( HTML ); # クローズ# HTMLとして解釈させるためのヘッダ出力
print "Content-type: text/html\n\n";foreach $line ( @html )
{# カウンタ置き換え用コメントタグを探す
$position = index( $line, $counter_comment );# 見つかったら...
if( $position != -1 )
{# カウンタのHTMLで置き換え
substr( $line, $position, length( $counter_comment ) ) = $image;}
print $line; # HTML行を出力
}
上記の内容を適当なファイル名でサーバにasciiモードで転送してpermissionを付けます。またカウンタファイルも同じディレクトリに置き、これにもpermissionを付けます。更に読み込ませるHTMLファイルも同じディレクトリに置き、permissionを付けます。cgiプログラムにはnobodyの--x属性が必要です。--xで動作しない場合はr-xでも試してみて下さい。カウンタファイルにはnobodyのrw-属性が必要です。HTMLファイルにはnobodyのr--属性が必要です。それぞれがどうしてそのようなpermissionを必要とするのかも考えてみて下さい。
いかがでしょうか。説明を読んだだけではやはりどうしても判らない部分が出てくると思うので、プログラムを眺めながらあちこちを少しずつ修正して実行してみましょう。
このトピックで取り上げたスクリプトはココからダウンロード出来ます。圧縮してあるので適当なディレクトリに解凍してください。