コボラーのためのPerl/CGI入門   (戻る)

この講座はCOBOLプログラマーがPerl(パール)で作るCGIをCOBOLプログラミングと比較しながら理解するためのものです。

ここでは、COBOLでよく見かける『COBOL言語説明書』的な説明はしません、興味のある方は本を買って下さい(コマンドの説明書としては、『初めてのPerl』がいいでしょう)

(注)私が言うCOBOLとは、NEC社ACOS2用COBOLのことです、よってEND−IF等のCOBOL85(同社ACOS2用)で使える
命令も考慮してありません。そのため以下の説明で、皆さんが使われているCOBOLとは異なることがあります。
またCOBOLを知らない人に、誤解されてしまうことがあるかもしれませんが、あしからず。


・Perlって何?
Perlの創造者は、Larry Wallという方ですが最初ファイルからレポートを作成しようとしていて、それまで使っていた言語では力不足となったので別の仕事にも流用できる汎用ツール を用意しようとしたのが始まりです。そして誰でもが無料で手に入れることができ、UNIX上でCコンパイラさえ備えていればほぼ例外なくPerlは動作すると書いてあります。また移植性が 高いため様々なシステムで利用されています(『初めてのPerl』より一部引用)
つまり、インターネットサーバーはUNIXが多いですからPerlを使えることができ、CGIを作るには比較的やさしい言語のようです。(私はCを知りませんがCOBOLを知っている人なら 簡単でしょう)


・プログラムの構造
COBOLには、見出し部、環境部、データ部、手続き部の4つのDIVISIONがありますがPerlにはありません。つまりいきなり手続き部から始めます。
但し、サーバーに設置するCGIについては、最初の行に #!/usr/local/bin/perlと記述してperlの存在場所を指定します。(サーバーによってこの記述と異なることがあります)
では、基本的な命令について比べてみましょう。

・Perl/CGIの構文
 構文の約束はありません、自由です。COBOLもそうですがプログラムは個性の固まりになりがちです。
 Perlも柔軟性に富んだ言語なのでCOBOL以上にわかりにくくすることが容易な言語と言えます。
 だからといって適当ではデバッグするのに苦労するだけでしょうからポイントをまとめると

 1.COBOLの構文に似せて作る。当たり前の話ですがコボラーならやっぱりCOBOL風に作った方が
   自分にとってわかりやすいでしょうから。
   ・最初にデータ部のように予め固定的に使う変数名を定義しておく

   ・次に、システム日付をとったり変数に初期値をセットしたり、そしてファイルをオープンしたりする
    という初期処理をもってくる。

   ・それから、基本的なメインルーチンを作り、そこからCOBOLのように階層構造を意識した形にする。
    つまり PERFORM MAIN-RTN THRU MAIN-EXT UNTIL 条件が成立するまで、という形を
    until (条件が成立するまで) { &MAIN_RTN } という感じです。
    但しCGIは帳票出力パターンではないためUNTILを使った繰り返しはあまり使いません。
    画面系のように押されたキーに対応した処理を起動させる形に近いと思われます。

   ・メイン処理の下に、各サブルーチンを置き処理毎に分ける。
    ここで注意することは、サブルーチンの終わりは } で区切られるのですが if文等も同じカッコなので
    これらが組合わさるとエラー(文法エラー)が起こりやすく失敗のもとになります。
    私はこのミスを防ぐために閉じカッコの後に○○の終わりと記述します。

   ・コボルと同様に処理は上から下に流れます、サブルーチンは呼ばれない限り処理はされません。
    よってメインルーチンを上の方に作り、それから下をすべてサブルーチンにすると上のメインルーチン
    の処理が終わるとプログラムが終了するイメージです。またコボルのSTOP RUNに対するものも必須
    ではありませんが、処理の最後がわかりやすいようにEXITを置いてもいいかもしれません。

 2.リロードさせて処理を進ませるので、初期処理後処理を分岐させるようにします。
   COBOLでは画面処理でも一度プログラムが走ればプログラムから抜けず前画面からのデータを受け
   取り次の処理に進みますがCGIではリロードをする毎に頭から処理を再実行します。
   よって次に渡すデータを意識しながらプログラミングしなければなりません。 何もしなければ変数はゼロ
       やスペースに戻ってしまいます。
       データを渡すために、hiddenパラメータというものを使います。

・スクリプトってプログラムですか?
 スクリプトの定義は知りませんが、perlはインタープリタで動きます。確かにコボルは事前にコンパイラで
 翻訳し、エラーをなくしてから実行しますから、いかにもプログラム言語らしいです。しかしperlは未定義エラーが
 あっても平気で実行していきますので、簡易言語程度に思う人もいるかもしれませんが、私はコボルと同じと思っ  ています。無意味なことですがコボルとPERLを同じ土俵で戦わせたらどうなるか、どちらが勝つと思いますか?
 PERLは、CGIの世界では覚えて損はない言語と思います、とにかく簡単です。でも将来はどうなるか私なんかに  はわかりません。もっとGUIの優れたものに代わるかもしれません。とりあえず私はJAVASCRIPTの本を2冊買
 い、ブラウザの制御はJAVASCRIPTで、サーバー処理はPERLでと思っていますが、どうなることやら。

・変数について
 1.COBOLのように、数字変数と文字変数の定義上の区別はありません、
   すべて$aのように書きます。 例えば、$a=10;とすれば数字変数、$a="10";とすれば文字変数となります
   がおもしろいことに$a=10;として if ($a eq "10") とすると真となり逆に、$a="10";として if ($a == 10)
   も真となります。
   これはCOBOLでのアンパックの数字が文字列としても認識できるのと似ているかもしれません。

 2.変数名の付け方に統一性を持たせる。
   もう皆さんは実行されていると思いますが、Perlは変数の事前定義がありません。よって変数名の
   パンチミスによっても動作がおかしくなりますから注意が必要です。
   各自の好みですが私は配列の添字変数は、$i $j $k を使い長い変数名は間違いの元になるので
   使いません。

 3.特殊変数というものがある・・・COBOLの世界にはないかな?
   COBOLの命令語は英語の単語のような感じですが、Perlではそれらの多くが記号で表現されます。
   良く使われるのが、$_ です、引数を省略するとこの変数にデータがセットされますが慣れると簡単で
   便利と感じます。

・書き方の注意
 
 1.変数には、小文字と大文字が使えます、当然 $a と $A とは異なります。
 2.命令は、小文字のみです。
 3.ファイルハンドラには両方使えますが、大文字が一般的です。
 4.1つのコマンドの終了はセミコロンです、これはコボルのピリオドと同じです
  5.コマンドと変数を続けて記述することができます
  例えば if ( $a eq "1" ) { $b = 1 } を続けてif($a eq "1" ){$b=1} と書けますが$aとeqをくっつけることは
  できません。
  反対に、語と語の間に半角スペースを適当に入れることができます、このスペースは実行時には無視され
  効率が落ちるということはないようですから、見やすいように適当に入れてください。
  6.HTML記述をprint命令で出力する場合の改行コード\nは毎行付ける必要はありませんが、最後の行には
  必ず付けること。

・ファイルの構造とアクセス
  一般に、Perl/CGIで使われるファイルには2つあります。
  1.通常のデータファイル(COBOLでの順編成ファイルのようなもの)
  2.DBM(UNIXシステムで使われているデータベースのひとつで、COBOLでいう索引編成ファイル
    のようなもの)
    但し、DBMはプロバイダにより使用できないことがありますのでここでは1のファイルについて
    説明します。

  私のファイルの使い方は、COBOLでいう可変長レコードファイルというようなものです、また
  COBOLと違ってファイルを作成した場合のアロケート情報というものがありません。
  ・1レコードは、改行コード(\n)が現れるまでのデータとなり、Perlでは1レコードというより
   1行(ライン)と言った感じです。
  ・ブロック長や登録データ数といったものはありません。(但しUNIXの世界ではあるかもしれませんが)
  ・1レコード中の項目の取り方には
   (1)項目と項目の間に区切りを示す変数を入れ、後で分解する。
   (2)COBOLのように項目の長さを決めデータ長で分解する。
   一般的なのは(1)です、これはPerlが文字列加工を得意としているからです。

  次に、アクセス方法(読み込み)ですがとりあえず私が使っているのは次の2つです。
   (1)ファイル内のすべてのデータを1回で読み込み、配列にセットします。
     open(DATA,"$file");
     @data = &ltDATA>
     close(DATA);
    ※結果は$data[0]、$data[1]、$data[2]、に入りますから後で繰り返し文を使い1レコードずつ処理
     します。COBOLではできない芸当です。
   (2)1件ずつ読みながら処理します。
     open(DATA,"$file");
     while (&ltDATA>) { この中で1レコードに対する処理をします、1レコードデータは $_ という
     変数に入っています  } 
     close(DATA);

   ここで、&ltDATA>はREAD命令です。

   次にファイルのオープンモードについて
   ・INPUT  ・・・ open(DATA,"$file");
   ・OUTPUT ・・・ open(DATA,">$file");
   ・EXTEND ・・・ open(DATA,">>$file");

   ファイルハンドラについて・・・上の例で使われてきた、DATAがファイルハンドラです、OPEN時に指定すれ
   ば後はこの名前を使って制御します、強いて言えばCOBOLでの内部ファイル名と外部ファイル名の関係
   でしょうか?
       COBOLではファイルのオープン、クローズ時に対象とするファイル名を指定しますが、Perlでも同様にopen
       やcloseの後の()内で対象とするファイルハンドラ名を指定します。

・IF文
   基本的にはCOBOLと同じですが、複雑な条件や実行文を記述することが容易です。
   このことは反対にエラーの原因となりますので実行文の中が長くなったときは処理単位 
   で切り離しサブルーチン化します。(COBOLと同じです)
   また、COBOLのIF文ではピリオドが終了を示すますが(皆さんもこのピリオドを
   間違ったところで付けてしまったことでバグったことがありませんか)
   Perlでは最後をカッコ }で閉じるだけですから簡単です。   

COBOLでは Perlでは
IF A = B
   PRINT "等しい"
ELSE
   PRINT "等しくない".
数値比較の場合
if ($a==$b) {print "等しい"}
else {print "等しくない"}

文字比較の場合
if ($a eq $b) {print "等しい"}
else {print "等しくない"}

IF A < B
   PRINT "小さい"
ELSE
IF A > B
   PRINT "大きい"
ELSE
   PRINT "等しい".
数値比較の場合
if ($a<$b) {print "小さい"}
elsif  ($a>$b) {print "大きい"}
else {print "等しい"}

文字比較の場合
if ($a lt $b) {print "小さい"}
elsif  ($a gt $b) {print "大きい"}
else {print "等しい"}

※elseif ではなく elsifになります

IF A = B AND A = C if ($a==$b && $a==$c)

AND 対応するものが && になります

IF A = B
    IF A = C
if ($a==$b) { if ($a==$c) {条件に一致した }}
IF A = 0 OR B = 0 if ($a==0 || $b==0) {条件に一致した }}

ORに対応するものが || になります

・配列について

        コボルでも同じことですが、Perlでもこの配列をうまく使えるかによってレベルの違いがでます、       
        Perlには、全データを一発で配列にセットしたり、繰り返し命令を使って容易に配列を作ったり
       する機能や文字列も添え字として使える連想配列というものがありますので、いろいろ応用が
       できるように身につけてください。

   2つの配列があります。
   (1)$a[0]、$a[1]、$a[2] のようなCOBOLと似た配列(添え字が0、1、2、となるもの)
     但し、OCCURS命令はなく、最初にいくつ使うという定義はありません。
     注、添え字は 0から始まります、0を使わないでもOKですが配列数には[0]も含まれます。
      $a[0]、$a[1]をまとめて@aで表します。

   (2)添え字に文字列を使える配列を連想配列と呼びます。
    COBOLにはないものです。
           $a{0} $a{1} $a{2} ...の形です、カッコが違いますね。この添え字には文字列が使えますので
          索引編成ファイルのキーの代わりに使うことができます。
          つまり、最初に全レコードを読みながら、ユニークなキー項目を添え字にしてレコードデータを
          セットすれば、キー項目からすぐに該当データをとることができます、もちろんこの方法では2重
          キーは1つのデータとなり処理できません。
         ※添え字に数字を使えば、$a{0} $a{1} $a{2}は$a[0]、$a[1]、$a[2] と同じ意味になります。
         また、添え字には変数が使えます(コボルもできます)、しかも一部を変数にすることもできます
       たとえば、$a{dd1} $a{dd2} $a{dd3} ...これを $a{dd.$no}の形にして $no を1から変化させれば
       添え字も dd1  dd2  dd3 となります。
          $a{0}、$a{1}をまとめて%aで表します。

・ソートについて
     
配列に対してソートをすることができます、方法はすごく簡単です。ソートさせたい配列をソート
      サブルーチンへ渡すだけです、例えば昇順なら  $a <=> $b とし、降順は $b <=> $a となります。
      また、配列データだけでなく添え字自身に対しても同様にできます。
      詳しくはサンプルスクリプトを見てください。このように配列に対してソートを組み合わせることにより
      見やすい形に変えることができます。

・サブルーチンについて
  
COBOLのPERFORM文に対するものが、&サブルーチン名です。
  呼ばれるサブルーチンのラベル名は、sub サブルーチン名 {     処理           } とします。
  このサブルーチンは多重化もできます、つまりサブルーチンの中から別のサブルーチンを呼び出し
  その中からまた別のサブルーチンを呼び出すができます。この構造はCOBOLと同様です。

・コマンド比較

COBOL Perl
MOVE  A   TO   B
MOVE A   TO  B  C  D
# 右辺から左辺にコピー(MOVE)される
$b = $a
# 一番右辺の内容がその左側にある項目すべてに
 コピーされる(この使い方はあまり見かけない)
$b=$c=$d=$a
ADD   1    TO  A
COMPUTE命令を使うと
COMPUTE  A =  A  +  1
# わかりやすい表現
$a=$a + 1
# 慣れてくると便利
$a++
SUBTRACT  1       FROM   A
COMPUTE命令を使うと
COMPUTE  A =  A  -  1
# わかりやすい表現
$a=$a - 1
# 慣れてくると便利
$a--
COMPUTE  A = B * C $a = $b * $c
COMPUTE  A = B / C $a = $b / $c
INSPECT  
STRING  

・デバッグについて
基本はCOBOLと同じでしょう。しかし大きな違いはCOBOLコンパイラは変数の未定義や2重定義、あるいはMOVEやCOMPUTE命令における変数のタイプミスマッチ、またまた構文のエラー等親切に教えてくれますが
PERLは変数に関して文字と数字の見分けがありません、そのため次のようなバグを起こしやすいです。

1.バンチミスによる変数の定義間違い・・・例えば $inputという変数を参照するとき$lnputと間違えた場合、i と l の違いに気がつかないと、$inputにデータが格納されていると思っても$lnputにはゼロやスペースが入り、処理はそのまま進みますから発見しにくくなります。

2.if の条件式に COBOLのくせで ($a = 10) としてしまう。正しくは ($a == 10) ですがJPERLでもエラーにはなりませんし、いい加減なのかPERLとしても実行してしまいます。例えば
if ($a=10) {print "hello" } の場合 $aの値とは無関係に hello と表示されます。

3.ファイル操作において、ファイルがなくてもエラーにはならずまるで処理したように次に進みます。これを防止するには、OPEN時にファイルがなかったら画面にエラー表示を出して自分にわかるようにする工夫が必要です。

4.タグ内に変数を使うとき、例えば print '<form method="post" action="' . $reload . '">' . "\n";の$reloadの置き方ですが、print の次を ” を使うか ’を使うかで変わります。これもエラーにはなりませんから注意が必要です。

5.COBOLでも同じですが、サブルーチンに処理を飛ばして 同じ変数名を使った結果、戻るときに飛ぶ前の値と違って戻りおかしくなります。これを防止するにはローカル変数LOCAL(変数定義)を利用してサブルーチン内だけで使用するようにします。


2001.1.13