解説 11月(2)

今回の学習項目です。

参考

CGIサーバーの使い方の補足をします。

[CGIサーバーにディレクトリを作成して、CGIファイルを整理するには?]

まず、ディレクトリを作成します。

[h208xxx@ls01 ~]% cd /stud_cgi/h208xxx    #CGIサーバーに移動する。

[h208xxx@ls01 h208xxx]% mkdir prog3ab     #prog3abという名前のディレクトリを作成する。

ディレクトリにアクセスするためには、そのディレクトリに「実行」権限を与える必要があります。 そうしなければ、その中のファイルを見ることができません。 さらに、CGIファイルやHTMLファイルにWebブラウザでアクセスするためには、「その他のユーザー」に「実行」権限を与える必要があります。

[h208xxx@ls01 h208xxx]% ls -l              #ディレクトリprog3abのパーミッションを確認。
drwx------                                 #その他のユーザーのアクセスが許可されていない。

[h208xxx@ls01 h208xxx]% chmod o+x prog3ab  #ディレクトリprog3abのパーミッションを変更。
                                           #("chmod 701 ディレクトリ名" でもよい。)

[h208xxx@ls01 h208xxx]% ls -l
drwx-----x                                 #その他のユーザーのアクセスが許可されている。

[h208xxx@ls01 h208xxx]% mv *.cgi prog3ab   #すべてのCGIファイルをprog3abの下に移動する。

別のディレクトリに移動したCGIファイルにWebブラウザでアクセスするときは、URLにもディレクトリ名を付け加えます。

http://cgi.st.chukyo-u.ac.jp/~h208xxx/prog3ab/hello.cgi

次のようなメッセージがブラウザに表示されたときは、ファイルだけでなく、ディレクトリのパーミッションも確認しておきましょう。

Access forbidden!
  You don't have permission to access the requested object.
  It is either read-protected or not readable by the server.
Error 403

[フォームを使って別のディレクトリのCGIファイルに値を送信するには?]

フォームを使って別のディレクトリに置かれたCGIファイルに値を送信するときは、<form>タグのaction属性に、CGIファイルへの相対パスを指定します。

#input.htmlと同じ場所にある、
#prog3abというディレクトリの下の、hello.cgiに送信する場合。
<form method="GET" action="prog3ab/hello.cgi">
  姓:<input type="text" name="sei"><br>
  名:<input type="text" name="mei">
  <input type="submit" value="送信">
</form>

リンクタグを使用してパラメータの値を渡すときも同じです。

#send.htmlと同じ場所にある、
#prog3abというディレクトリの下の、hello.cgiにパラメータを渡すリンク。
<a href="prog3ab/hello.cgi?sei=Sirai&mei=Hidetosi">送信</a>

相対パスですので、例えば、「一つ上のディレクトリ」を表す ".." を使用することもできます。

#input.htmlの置かれた場所の、
#一つ上のディレクトリの下の、h208yyyというディレクトリの中にある、hello.cgiに送信する場合。
<form method="GET" action="../~h208yyy/hello.cgi">
  姓:<input type="text" name="sei"><br>
  名:<input type="text" name="mei">
  <input type="submit" value="送信">
</form>

別のサーバー上のCGIプログラムに対して値を送信する場合は、URLを記述する必要があります。

#Googleに検索ワードを送信する場合。
<form method="GET" action="http://www.google.co.jp/search">
  検索ワード:<input type="text" name="q">
  <input type="submit" value="検索">
</form>


課題1

課題1は前回の課題の発展課題です。
前回の課題を十分に理解していないと、この課題はうまく学べない可能性があります。 前回の課題が完了していない人は、まず前回の課題を行って、理解を深めてからこの課題を行うのがよいでしょう。

課題1-1

値を受け取るCGIプログラムの練習課題です。
課題1-1では、入力された生年月日を元に、生まれた日の曜日を判定するCGIプログラムを作成することにします。

生年月日入力ページ

課題1-1-0

まず、ターミナル上で、何年何月何日と入力すると、それが何曜日かを判定し、出力するプログラムを作成しなさい。
例えば、次のようなやりとりをするプログラムを作りなさい。
曜日を判定します。
日付を入力してください。
年:1990
月:8
日:15

この日は水曜日です。

曜日を判定する部分では、Timeクラスのmktimeメソッドや、wdayメソッドを使用するとよい。

[Timeクラスのmktimeメソッド]

Timeクラスのmktimeメソッドを使用すると、任意の時刻オブジェクトを生成することができます。

birthday = Time.mktime(1989, 11, 2)    #1989年11月2日のTimeオブジェクトを生成
p birthday
Thu Nov 02 00:00:00 +0900 1989

[Timeクラスのwdayメソッド]

Timeクラスのオブジェクトにwdayメソッドを働かせると、曜日を表す0以上6以下の数が返ってきます。

t = Time.now
p t.wday
1    #1は月曜を表す。

[ヒント:配列を上手に利用しましょう]

Timeクラスのwdayメソッドの返す値は0以上6以下の数です。 wdayメソッドの返す値に応じて、「日曜日です」とか「月曜日です」と表示する必要がありますが、if文を使って7通りの分岐を書くのは面倒ですね。 こういうときは、配列を上手に利用することで、プログラムを簡単に書くことができます。

youbi = ["日", "月", "火", "水", "木", "金", "土"]

p youbi[1]
"月"

課題1-1-1

次のような、パラメータに指定された日付の曜日を判定し、その結果をHTML形式の文字列として出力するCGIプログラムを作成しなさい。

曜日判定プログラム (パラメータの年の値に1989を、月の値に11を、日の値に2を指定しています。)

プログラムを作成したら、まずオフラインで実行できることを確認しなさい。 実行する際に、年、月、日の値をパラメータとして入力すること。

[CGIのプログラムでパラメータの値を取得するには]

プログラムに送られたパラメータの値を取得するときは、次のようにCGIクラスを使用 する必要がありました。

require "cgi"    #CGIクラスを読み込む。

cgi = CGI.new    #CGIクラスのインスタンスオブジェクトを生成。
                 #(このときパラメータが受け取られる。)
変数1 = cgi["パラメータ名1"]  #生成したCGIオブジェクトからパラメータの値を取得。
変数2 = cgi["パラメータ名2"]  #生成したCGIオブジェクトからパラメータの値を取得。
変数3 = cgi["パラメータ名3"]  #生成したCGIオブジェクトからパラメータの値を取得。
...

[オフラインでの実行時にパラメータを入力するには]

プログラムをオフラインで実行するときに、CGIオブジェクトを生成する部分が実行されると、パラメータの入力が求められます。 次のようにしてパラメータを入力し、ブラウザからアクセスするときと同じように、CGIプログラムにパラメータを渡しましょう。

[h208xxx@ls01 h208xxx]% ./hello.cgi    #Enterキーを押して実行開始。
Content-type: text/html; charset=euc-jp

(offline mode: enter name=value pairs on standard input)
sei=Sirai&mei=Hidetosi    #Enterキーを押して改行する。
#Ctrl + d を押して実行再開。

オレンジ色の文字の部分はキーボードからの入力です。 入力し終わったら一度改行して、Ctrlキーを押しながらDキーを押してください。 プログラムの実行が再開されます。

他にも次のようなパラメータの渡し方があります。

[h208xxx@ls01 h208xxx]% ./hello.cgi sei=Miyake mei=Yoshio    #Enterキーを押して実行開始。
[h208xxx@ls01 h208xxx]% ./hello.cgi    #Enterキーを押して実行開始。
Content-type: text/html; charset=euc-jp

(offline mode: enter name=value pairs on standard input)
sei=Sirai    #Enterキーを押して改行する。
mei=Hidetosi    #Enterキーを押して改行する。
#Ctrl + d を押して実行再開。

HTML文書が正しく出力されることを確認したら、作成したCGIファイルにブラウザでアクセスして、ページが正しく表示されることを確認しなさい。 オフラインでの実行時と同様に、パラメータを渡して実行すること。

[ブラウザから実行する際にパラメータを渡すには]

CGIプログラムをブラウザから呼び出して実行するときは、次のようにパラメータを入力することができました。

http://cgi.st.chukyo-u.ac.jp/~h208xxx/ファイル名?パラメータ名1=値1&パラメータ名2=値2...

ブラウザからの実行が正しく行われていることを確認したら、作成したプログラムと、CGIファイルのURLを提出しなさい。 (URLには適当な生年月日のパラメータも付けておいてください。)

課題1-1-2

上で作成したCGIプログラムに、生年月日の値を渡すためのHTMLファイルを作成しなさい。

生年月日入力ページ

フォームを使用して年、月、日の値を入力できるようにしてください。 作成したHTMLファイルの内容(HTML文書)と、HTMLファイルのURLを提出すること。

[フォームを使用するには]

Webページでフォームを使用するときは、次のように<form>タグを使います。

<form method="GET" action="送信先ファイル名">
  フォーム部品
  フォーム部品
</form>

フォーム部品のところには、テキスト入力フィールドや実行ボタンなどを表示するためのタグを記述する必要があります。

[テキスト入力フィールド]

フォーム部品のテキスト入力フィールドを使用するときは、<form>タグの間に<input>タグを書き、type属性に "text" を指定します。

<form method="GET" action="送信先ファイル名">
  <input type="text" name="パラメータ名">  #テキスト入力フィールド
  ...
</form>

name属性には、値を送信するときに付けるパラメータ名を記述します。

また、次のようにsize属性に数値を指定することで、テキスト入力フィールドの横幅を変更することができます。

<input type="text" name="パラメータ名" size="4">  #横幅4のテキスト入力フィールド

入力された値を送信するためには、フォーム部品の実行ボタンが必要になります。

[実行ボタン]

フォームに入力した値を送信するためには、フォーム部品の実行ボタンを用意する必要があります。 実行ボタンを表示するときは、<form>タグの間に<input>タグを書き、type属性に "submit" という値を指定します。

<form method="GET" action="送信先ファイル名">
  フォーム部品
  <input type="submit" value="ボタンに表示する文字">  #実行ボタン
</form>


課題1-2

課題1-2では、テキスト入力フィールドに色を入力して送信すると、そのページの背景色が変わるCGIプログラムを作成します。

背景色を変更できるCGIプログラム

上のページは、色が指定されなかった場合に、"skyblue" という色を背景色として使用するように作られています。

課題1-2-1

まず、上のCGIのページがどのように作成されているか調べてみましょう。
次の3つの場合において、ブラウザのアドレスバーに表示されるURLと、Webページのソースの<body>タグがどのように変化するか調べて答えなさい。
URLは、ファイル名とパラメータの部分だけを答えればよい。 <body>タグは、ページの背景色を指定するbgcolor属性の値に注目して答えること。

1. 最初にページを開いたとき
 ・URLのファイル名とパラメータは○○○
 ・<body>タグのbgcolor属性の値は×××

2. テキスト入力フィールドに「red」と入力して「変更」ボタンを押した後
 ・URLのファイル名とパラメータは○○○
 ・<body>タグのbgcolor属性の値は×××

3. テキスト入力フィールドに何も入力せずに「変更」ボタンを押した後
 ・URLのファイル名とパラメータは○○○
 ・<body>タグのbgcolor属性の値は×××

[Webページのソースを表示するには]

Internet Explorerの場合、メニューバーの「表示」から「ソース」を選択することで、開いているWebページのソース(HTML文書)を表示することができます。

ただし、Internet Explorerがソースの表示に使用する「メモ帳」は、EUC-JPで書かれた文字を正しく表示することができません。 そのため、表示されたHTML文書の日本語の部分は文字化けしてしまいます。

[<body>タグのbgcolor属性]

<body>タグのbgcolor属性に色を指定することで、Webページの背景に色を付けることができます。

<body bgcolor="red">    #背景色を赤色にする。

</body>

課題1-2-2

次に、上の3つの場合において、プログラムの受け取るパラメータの値がどのように変化するか調べてみましょう。
まず、次のようなCGIプログラムを作成しなさい。

#!/usr/bin/ruby
print "Content-type: text/html; charset=euc-jp\n\n"
$KCODE = "e"

require "cgi"

cgi = CGI.new
p cgi["color"]    #CGIオブジェクトから"color"という名前のパラメータの値を取得して表示。

作成したら、プログラムをオフラインで実行し、次のように入力するパラメータを変えながら、それぞれどのような値が取得されるか調べて報告しなさい。

#「最初にページを開いたとき」をオフラインで再現

[h208xxx@ls01 h208xxx]% ./change_color.cgi
Content-type: text/html; charset=euc-jp

(offline mode: enter name=value pairs on standard input)
#パラメータを入力しない。

#「色が送信されたとき」をオフラインで再現

[h208xxx@ls01 h208xxx]% ./change_color.cgi
Content-type: text/html; charset=euc-jp

(offline mode: enter name=value pairs on standard input)
color=red    #パラメータ名と値を入力する。

#「未入力で送信されたとき」をオフラインで再現

[h208xxx@ls01 h208xxx]% ./change_color.cgi
Content-type: text/html; charset=euc-jp

(offline mode: enter name=value pairs on standard input)
color=       #パラメータ名のみ入力する。

課題1-2-3

上でわかったことを参考にして、ページの背景色を変更することのできるCGIプログラムを作成しなさい。 色が指定されなかった場合の背景色は、自分の好きな色を使用すればよい。 作成したプログラムと、CGIファイルのURLを提出すること。

[ヒント]

色が指定されなかったときは、colorという名前のパラメータの値が "" (空の文字列)になりますので、その場合は自分の好きな色を背景色として使うようにしましょう。
色が指定されたときは、その値をそのまま背景色として使えばよい。

[具体的にはどうやるの?]

CGIオブジェクトからcolorという名前のパラメータの値を取得し、その値が "" かどうかによって場合分けします。

cgi = CGI.new

if(cgi["color"] == "")  #もし色が指定されなかったら
  colorName = 自分の好きな色
else
  colorName = パラメータから受け取った色
end

[Webページで使用することのできる色は]

たくさんあります。書き切れないので自分で調べてください。

調べてみる

ちなみにHTMLでは、色の名前ではなく、RGB値を16進数で記述して色を指定することもできます。 例えば "red" の場合、RGB値は "#FF0000" というように表現することができます。

課題1-2-4

テキスト入力フィールドには初期値を指定することができます。

[テキスト入力フィールドに初期値を指定するには]

次のように、<input>タグにvalue属性を書き加えることで、テキスト入力フィールドに初期値を設定することができます。

<input type="text" name="パラメータ名" value="初期値">

ページが表示されると、指定された初期値がテキスト入力フィールドに記入されています。

    #初期値の記入されたテキスト入力フィールド

この値は初期値ですので、実行ボタンを押す前に別の値に書き換えることができます。

この機能を使って、背景色を変更すると、テキスト入力フィールドにその色の名前が記入されるCGIプログラムを作成しなさい。

色の名前がテキスト入力フィールドに記入されるCGIプログラム

ついでに文字の色も変更できるようにしなさい。 作成したプログラムと、CGIファイルのURLを提出すること。

[文字の色を変更するには]

Webページに表示する文字の色を変更するときは、<font>タグのcolor属性 に色の名前(もしくはRGB値)を指定します。

<font color="色">
ここに書かれている
文字の色が変わります。
</font>

課題2-0

以前の課題(10月の課題(6))では、アカウントを扱うプログラムを関数を使って実現しました。 今回は、まず、前回作成したプログラムに対してテストを行い、プログラムが正しく作られているか検証することにします。

一見正しく動作するように見えるプログラムでも、どこにバグが潜んでいるかわかりません。 それほど複雑でないプログラムでも、思わぬところで間違った処理をしていることはよくあります。

また、手順書通りにプログラムを作成することができたとしても、安心はできません。 そもそも手順書に誤りがないとも限りませんので、プログラムが完成したら充分なテストを行い、動作を検証する必要があります。 正確なプログラムを作成する上でテストとは、手順書の作成やプログラムを書くのと同じくらい重要な工程なのです。

課題2-0-1

完成したプログラムに対してどのようなテストを行えばよいか、見極めるのは容易ではありません。 せっかくテストを行っても、テストの内容に漏れがあれば、重大なバグを見落としてしまうかもしれません。 前回の課題1-1のアカウントチェックのプログラムであれば、最低でも次の4通りの検証を行う必要がありそうです。

・パスワードが正しい場合の検証
  アカウント:中京太郎
  パスワード:aaa123

・2つ目以降のデータを正しく処理できているか
  アカウント:豊田二郎
  パスワード:tyt260

・パスワードが間違っている場合の検証
  アカウント:中京太郎
  パスワード:aaabbb

・アカウントが存在しない場合の検証
  アカウント:中京三郎
  パスワード:aaabbb

前回の課題1-1で作成したプログラムに対して、上の4通りのテストを行い、結果を報告しなさい。 テストが通らなかった場合は、プログラムを修正して、修正したプログラムも提出しなさい。

[ヒント:豊田二郎が「ありません」となってしまう人は]

アカウントデータの2つ目に登録されているはずの「豊田二郎」でログインすると「ありません」と表示されてしまう人は、次のように、「アカウントがない」ことを示すnilを返す部分を、while文の中に書いていないでしょうか。

def verifyAccount(account, password)
  ...

  while(アカウントデータの数だけ繰り返す)
    アカウントデータを一つ取り出す。
  
    if(アカウント名を比較)
      #アカウント名が一致した場合
      if(パスワードを比較)
        #パスワードが一致した場合
        return(true)
      else
        #パスワードが一致しない場合
        return(false)
      end
    else
      #アカウント名が一致しない場合
      return(nil)
    end
  
    ...
  end

  ...
end

「豊田二郎」でログインするときのことを考えてみましょう。 上のプログラムでは、1つ目のデータの「中京太郎」とアカウント名が一致しなかった時点で、「豊田二郎は存在しない」と判断されてしまいます。

本来なら、「アカウントがない」ことが判明するのは、すべてのアカウントデータを一通りチェックし終えた後のはずですので、nilを返すreturn文は、while文の外(endの後)に書く必要があります。 そうすることで、繰り返しの途中でreturnによって関数から抜け出さなかったときだけ、「アカウントがない」ことを示すnilを返すようになります。

課題2-0-2

前回の課題1-2のアカウント登録のプログラムに対して、どのようなテストを行う必要があるか考え、テストの項目を記述しなさい。
記述したら、前回の課題1-2で作成したプログラムに対して、自分で用意したテスト項目に従ってテストを行い、結果を報告しなさい。 テストが通らなかった場合は、プログラムを修正して、修正したプログラムも提出しなさい。

[ヒント]

先ほどと同じようなテスト項目に加え、今度は、ファイルへの書き込みが正しく行われることも確認する必要がありそうです。 例えば、一度登録したはずの「八事花子」というアカウントを、もう一度登録できてしまう場合は、ファイルへの書き込みが正しく行われていないことになります。


課題2-1

前回の課題ではアカウントを扱うプログラムを関数を使って実現しました。 今回は同じようなプログラムをハッシュを使って実現します。
ハッシュを使用すると、たくさんの情報の中から必要なものを簡単に探し出すことができます。 情報システムを構築する際には、そのような状況は珍しくありません。 ハッシュを上手に使えるようになれば、情報システムの構築にきっと役立つでしょう。 今回はそのための練習として、アカウントを管理するプログラムを、ハッシュを使って作成してみましょう。

まず、ハッシュの扱い方を簡単に復習しておきましょう。なお、 ハッシュの扱い方の説明は、講義「アルゴリズムとデータ構造」の6月5日のページにあります。

以下の課題を解きなさい。

課題2-1-1

次のハッシュには、ユーザーのアカウント名とパスワードが登録されています。

account_hash = {"中京太郎" => "aaa123", "豊田二郎" => "tyt260"}

このハッシュから、「中京太郎」というアカウント名に対応するパスワードを取り出し、pを使って表示しなさい。

課題2-1-2

上のハッシュから、「中京三郎」というアカウント名に対応するパスワードを取り出そうとすると、どのような値が取り出されるか、pを使って表示して確かめなさい。

課題2-1-3

上のハッシュに、「八事花子」というアカウント名を「ygt875」というパスワードで登録しなさい。 登録したら、pを使ってハッシュを表示し、正しく登録できていることを確認しなさい。

課題2-1-4

アカウントデータを次のようなファイルから読み込み、空のハッシュに登録しなさい。 登録したら、pを使ってハッシュを表示し、正しく登録できていることを確認しなさい。

アカウントデータ(EUC-JPで保存されています。)

課題2-2

前回に作成したアカウントを管理するプログラムを、ハッシュを使って作成してみましょう。 アカウント名とパスワードは、前回と同じように次のようなファイルで管理します。

アカウントデータ(EUC-JPで保存されています。)

今回は関数を定義する必要はありません。 以下の課題を解きなさい。

課題2-2-1

アカウントとパスワードを検証するプログラムを、ハッシュを使って作成しなさい。

*** ログインしてください。自分のアカウント名とパスワードを入力してください。  ***
アカウント:中京太郎
パスワード:aaa123
ようこそ、中京太郎さん。それではゲームを始めましょう。

アカウントがない場合は次のような出力を出すことにします。

*** ログインしてください。自分のアカウント名とパスワードを入力してください。  ***
アカウント:中京三郎
パスワード:aaabbb
中京三郎というアカウントはありません。

アカウントがあっていて、パスワードが合わない場合は次のようなメッセージを出すことにします。

*** ログインしてください。自分のアカウント名とパスワードを入力してください。  ***
アカウント:中京太郎
パスワード:aaabbb
パスワードが間違っているようです。

[ヒント:プログラム作成の手順]

ファイルの中に格納されている個々のアカウントデータをいったん配列の中に読み出し、その配列の中からアカウント名とパスワードを取り出してハッシュに登録します。 すべてのアカウントデータをハッシュに登録したら、入力されたアカウント名に対応するパスワードを、ハッシュから取り出して検証します。

課題2-2-2

アカウントがない場合に新しくアカウントを作るプログラムを、ハッシュを使って作成しなさい。

アカウントを作ります。アカウント名とパスワードを入力してください。
アカウント:八事花子
パスワード:ygt875
アカウントを作成しました。

新しいアカウントは前回と同様にファイルの終わりに付け加えることにします。
アカウントがすでにある場合には、次のようなメッセージを出すことにします。

アカウントを作ります。アカウント名とパスワードを入力してください。
アカウント:中京太郎
パスワード:aaabbb
中京太郎というアカウントはすでにあります。別なアカウント名を使ってください。

課題2-3

課題2-3では、課題2-2で作成したアカウントを管理するプログラムをCGIのプログラムに作り変えて、Web上で動作するアプリケーションとして実現します。 次のようなアプリケーションを作成します。適当なアカウントを作成してかまいませんので、動かしてみてください。

ログインページ

以下の課題を解きなさい。

課題2-3-1

まず、アカウントとパスワードの検証部分を作成してみましょう。
最初に表示されるログインページ(login.html)と、ログインボタンをクリックすると表示されるアカウント認証ページ(verify.cgi)を作成しなさい。
アカウント認証ページでは、アカウントがない場合やパスワードが合わない場合など、結果に合わせて表示するメッセージを変更すること。
完成したら、login.htmlのURLとverify.cgiのプログラムを提出しなさい。

[パスワード入力フィールド]

フォーム部品のパスワード入力フィールドを使用するときは、<input>タグ のtype属性に "password" と指定します。

<form method="GET" action="送信先">
  <input type="text" name="パラメータ名">       #テキスト入力フィールド
  <input type="password" name="パラメータ名">  #パスワード入力フィールド
  <input type="submit" value="ログイン">        #実行ボタン
</form>

パスワード入力フィールドには、全角文字を入力することができません。 また、入力時に、文字がそのまま画面に表示されないようになります。

課題2-3-2

新しいアカウントを登録する部分を作成してみましょう。

アカウント入力ページ(input.html)

ログインページの「アカウントを作成する」をクリックすると表示されるアカウント入力ページ(input.html)と、作成ボタンをクリックすると表示されるアカウント作成ページ(create.cgi)を作成しなさい。
アカウント作成ページでは、アカウントが既に登録されている場合は作成を中止し、別なアカウントを使うようにメッセージを表示すること。
完成したら、input.htmlのURLとcreate.cgiのプログラムを提出しなさい。

プログラミングIIIのホームに戻る