コンピュータリテラシー演習のページへ
Rubyによるはじめてのプログラミング

例題:アクセスカウンタ

以下のプログラムはアクセスカウンタとして動作するものです。

counter.cgi - アクセスカウンタのプログラム
 1: #!/usr/bin/env ruby
 2: print "Content-Type: text/html\n\n"
 3: CFILE="counter.dat"
 4: # カウンタの値を読み込む
 5: begin
 6:   fp = open(CFILE, "r")
 7:   counter = fp.gets.to_i + 1
 8:   fp.close
 9: rescue
10:   counter = 1
11: end
12: # カウンタの値を書き込む
13: fp = open(CFILE, "w")
14: fp.print counter
15: fp.close
16: # カウンタの値をHTML形式で表示する
17: print "<HTML><BODY>\n"
18: print "counter=#{counter}\n"
19: print "</BODY></HTML>\n"

このプログラムは、counter.dat というファイルにカウンタの値(整数値)を格納し、 実行されるたびにカウンタの値を1つ増やしています。このプログラムは大まかに4つの処理から成り立っています。

以下は、それぞれの詳細について説明します。

httpのヘッダを出力する

前の例と同様にhttpのヘッダで、html形式であることを標準出力に出力します。

ファイルからカウンタの値を読み込み、1を加える

カウンタの値を入れておくファイルcounter.datを読み込みます。 ファイルを読み込むには、まず「ファイルを開く」という操作をします。 ファイルを開くにはopenメソッドを使います。Rubyでopenメソッドを使うやり方は色々とありますが、この場合は、C言語など他の言語に近い使い方をします。

fp = open(ファイル名, "r")

fpはopenメソッドが返す値が入ります。この値はファイルを操作するために後で使うため、fpに代入しておくのです。 ファイル名には、文字列でこれから開くファイル名を指定します。この場合は、2行目で"counter.dat"をCFILEに代入して、そのCFILEを使っています。つまり、6行目では"counter.dat"を開こうとします。"r"とあるのは、readの頭文字で、ファイルを読み込むために開くことを意味します。

ファイルがうまく開けたら、7行目でファイルから一行読み込みます。fp.getsというところに注目してください。getsはキーボード(標準入力)から一行入力するメソッドでした。ファイルの場合は、同じようにfp.getsと書けば、一行読み込むことができるのです。 ただし、標準入力の場合は、「ファイルを開く」という操作が必要ない(最初から開いている)のにたいして、ファイルの場合は読み込む前に必ずファイルを開く操作が必要なところが違います。

一行入力したものは、.to_i で整数値に変換して、さらに1を足したものを変数counterに代入しています。

8行目では、「ファイルを閉じる」という作業をしています。一度開いたファイルは使い終わったら「閉じる」ことが必要です。実はプログラムが終了したら、自動的に閉じてくれるのですが、このプログラムのように再度開くこともあるので、きちんと閉じる習慣をつけてください。8行目が終るとrescue 〜 end を飛ばして13行目に移ります。

なお、ファイルが開けないということがあります。例えば、ファイルが存在しないとファイルは開けません。この場合、ファイルが開けないと、rescue 〜 end の間、つまり10行目が実行されます。つまり、変数counterの値を1にします。これは「例外」という仕組みを使ったものですが、説明は省略します。今の時点では、このようなものと思ってください。

ファイルにカウンタの値を書きこむ

このプログラムでは、counter.dat というファイルにカウンタの値を記録して、 次回にcounter.cgiを読んだ時に使います。つまり、counter.datに書き込まないことにはうまく機能しないのです。

ファイルに書き込むのは、ファイルを読み込む操作に非常に似ています。 13行目でファイルを開きます。読み込みときと違うのは、"r"が"w"になっていることです。 "w"はwriteの頭文字です。つまり、書き込むためにファイルを開きます。 なお、書き込むためのファイルがすでに存在する場合には、その内容を全て消してから書き込みます。この場合、counter.datが存在していても、counter.datの内容は全て消されるのです。

14行目で、ファイルカウンタの値を書きこんでいます。読み込みでgetsを使ったように、ここではprintを使っています。画面(標準出力)に出力するには、printメソッドを使いましたが、ファイルに書きこむ場合は、fp.print とします。 15行目で、ファイルを閉じています。ファイルを閉じるのを忘れないように心がけてください。

HTMLとしてカウンタの値を出力する

ここは、以前に習った通りです。HTML形式で、「counter=カウンタ値」という内容を表示しています。18行目には文字列中に変数counterの値を入れ込んでいますが、 print "counter = ", counter, "\n"としても同じです。

このプログラムの留意点

このプログラムには、1つ問題があります。アクセスが集中すると、カウンタの値が1に戻ってしまいます。この問題を解消するには排他制御を行う必要があり、普通はファイルのロックという作業を行います。プログラムがさらに複雑になるので、ここではそこまではやりませんが、実用的なCGIプログラムを作成するには欠かせない事項です。興味のある人は関連する文書を探して勉強してみてください。

HTMLファイルの中にカウンタを埋め込みたい

以上で、Webページを何回アクセスしたかを示すアクセスカウンタが作成できましたが、 通常アクセスカウンタは、長いHTMLファイルの中に埋め込んで使いたいものです。 その方法にはいろいろとありますが、ここでは、CGIプログラムを呼び出して、 そのプログラムの中から表示したいhtmlファイルを読み込み、指定した場所にアクセスカウンタを埋め込んで表示するようにします。

CGIの仕組みそれ自身は、このような用途にはあまり向いていません。 SSIなどを使うのが便利ですが、セキュリティの問題になりやすいので薦めません。 Rubyの場合は、eRubyという仕組みを使うのがこのような用途に向いています。

以下では読み込むファイルを、esample.html とし、そのファイルの中に、 ACCESS_COUNTERという行があれば、それをアクセスカウンタの値に付け替えるようにしています。

ecounter.cgi - HTMLの中にアクセスカウンタを埋め込む
#!/usr/bin/env ruby
print "Content-Type: text/html\n\n"
CFILE="ecounter.dat"
HFILE="esample.html"
# カウンタの値を読み込む
begin
  fp = open(CFILE, "r")
  counter = fp.gets.to_i + 1
  fp.close
rescue
  counter = 1
end
# カウンタの値を書き込む
fp = File.open(CFILE, "w")
fp.print counter
fp.close
# HTMLファイルを読み込み、その内容を出力する
begin
  fp = open(HFILE, "r")
  while s = fp.gets do # ファイルから1行読み込む
    # もし"ACCESS_COUNTERなら"、カウンタの値に入れ替える
    if (s.chomp == "ACCESS_COUNTER") then
      s = "#{counter}\n"
    end
    print s # 1行分を標準出力に出力
  end 
  fp.close
rescue
  print "<HTML><BODY>File not Found</BODY></HTML>\n"
end

esample.html - 使用するHTMLファイルの例

課題

  1. 上記counter.cgiを使ってアクセスカウンタのCGIを実行せよ。
  2. 上記ecounter.cgiを使って、自分のホームページにアクセスカウンタを埋め込むCGIを作成せよ。ただし、ホームページのファイルはコピーして別に用意してよい。
  3. 上記ecounter.cgiを元に、表示するHTMLの中にCURRENT_TIMEという行があれば、その行を現在の時刻に入れ替えて表示するCGIを作成せよ。

東京情報大学情報システム学科
大見 嘉弘(Yoshihiro OHMI)