1. はじめに
Paiza Cloud上にCOBOL環境を構築します。あわせてHerculesというホストコンピューターのエミュレータもご紹介します。Windows上でMVSを動かしてJCLからジョブを実行できます。
1.1 用意するもの
- PaizaCloudのアカウント
1.2 構築内容
- 対象はOpensource COBOL V1.5.1J(UTF-8) + VBISAMです
- 画面のカーソルライブラリはncurseです(DISPLAY LINE COL での日本語は文字化けします)
1.3 参考URL
2. 環境構築
2.1 PaizaCloudの起動
いつものようにPaizaCloudを使って環境構築を行います。
2.2 サーバー作成
今回は特に他のアプリケーションをインストールする必要はありません。何も選択せずに新規サーバ作成ボタンを押して下さい。
2.3 アプリ構築
サーバが構築できたら、ターミナルを開いて下記のコマンドを投入してください
gmpの導入
数値計算ライブラリを導入します。
cd
wget https://gmplib.org/download/gmp/gmp-6.2.1.tar.bz2
tar jxvf gmp-6.2.1.tar.bz2
cd gmp-6.2.1/
./configure
make
sudo make install
ncursesの導入
画面カーソルのライブラリを導入します。utf-8対応版のncursesもあるようですが、調べ切りませんでした。
他に検索するとワイド文字対応のncurseswというライブラリを使う方法が出てきますが、こちらは他試したところ私の手順では上手く行きませんでした。
ということで、DISPLAY LINE COL、ACCEPT LINE COLでの日本語は入出力できませんが、ファイルやコンソール出力ではキチンと日本語表示されます。
cd
wget -nc ftp://ftp.gnu.org/gnu/ncurses/ncurses-6.1.tar.gz
tar -xf ncurses-6.1.tar.*
cd ncurses-6.1/
./configure
make
sudo make install
OpenSourceCOBOLの導入(UTF-8版)
OSSのサイトからダウンロードしたあと、まずはVBISAMをコンパイルします。次に本体のコンパイルを実施します。
cd
wget "https://www.osscons.jp/osscobol/files/?action=cabinet_action_main_download&block_id=414&room_id=21&cabinet_id=11&file_id=406&upload_id=911" -O opensource-cobol-1.5.2J.tar.gz
tar -xvf opensource-cobol-1.5.2J.tar.gz
cd
cd opensource-cobol-152J_utf8/vbisam/
./configure
make
sudo make install
cd
cd opensource-cobol-152J_utf8/
./configure --with-vbisam --enable-utf8
make
sudo make install
導入後のセルフチェック
正しく動作するか確認します
cd
cd opensource-cobol-152J_utf8/
export LD_LIBRARY_PATH=/usr/local/lib
sudo ldconfig
cd tests
make check
cobc -V
cd
3. COBOLサンプルプログラム
3.1 HELLO WORLD
定番ということでhello worldです。
下記のファイルをhello.cobという名前で作成してください
000010 IDENTIFICATION DIVISION.
000020 PROGRAM-ID. HELLO.
000030 ENVIRONMENT DIVISION.
000040 DATA DIVISION.
000050 PROCEDURE DIVISION.
000060 MAIN.
000070 DISPLAY "HELLO WORLD".
000080 STOP RUN.
これをコンパイルして実行します。
ちなみに、このソースファイルの最終行には空行が必須です。空行はスペースやタブがあっても駄目です。この空行がないとhello.cob:7: Warning: Line not terminated by a newlineエラーになります。
cobc -x hello.cob
./hello
HELLO WORLD
3.2 ファイル入出力の例
こちらも定番だと思いますが、ファイルの入出力です。商品マスタと売上明細を突き合わせてレポートファイルを作成しています。
- 商品マスタを商品コードでソートする
- 売上明細も商品コードでソートする
- 両ファイルを一行ずつ読む
- 商品マスタの商品コードが売上明細の商品コードより大きければ、売上明細なしと判定
- どちらも同じ商品コードであれば、レポートを1行出力して次の売上明細を読み込む
- 商品マスタの商品コードより売上明細の商品コードが大きければ、次の商品マスタのレコードを読み込む
IDENTIFICATION DIVISION.
PROGRAM-ID. ITEM.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT MASTER-INPT ASSIGN TO "./MASTER.TXT".
SELECT MASTER-SORT ASSIGN TO "./MASTER-SORT.TXT".
SELECT MASTER-TEMP ASSIGN TO "./MASTER-TEMP.TXT".
*
SELECT SALES-INPT ASSIGN TO "./SALES.TXT".
SELECT SALES-SORT ASSIGN TO "./SALES-SORT.TXT".
SELECT SALES-TEMP ASSIGN TO "./SALES-TEMP.TXT".
*
SELECT MASTER-FILE ASSIGN TO "./MASTER-SORT.TXT".
SELECT DETAIL-FILE ASSIGN TO "./SALES-SORT.TXT".
SELECT REPORT-FILE ASSIGN TO "./REPORT.TXT".
*
DATA DIVISION.
FILE SECTION.
* 商品マスタ(ソート前)
FD MASTER-INPT.
01 M-INPT-RECORD.
05 M-INPT-CODE PIC X(07).
05 M-INPT-COMMA1 PIC X(01).
05 M-INPT-NAME PIC N(09).
05 M-INPT-COMMA2 PIC X(01).
05 M-INPT-PRICE PIC 9(06).
05 M-INPT-CRLF PIC X(02).
* 商品マスタ(ソート後)
FD MASTER-SORT.
01 M-SORT-RECORD PIC X(44).
* 商品マスタ(処理用)
SD MASTER-TEMP.
01 M-TEMP-RECORD.
05 M-TEMP-CODE PIC X(07).
05 M-TEMP-COMMA1 PIC X(01).
05 M-TEMP-NAME PIC N(09).
05 M-TEMP-COMMA2 PIC X(01).
05 M-TEMP-PRICE PIC 9(06).
05 M-TEMP-CRLF PIC X(02).
*
* 売上明細(ソート前)
FD SALES-INPT.
01 S-INPT-RECORD.
05 S-INPT-DATE PIC X(08).
05 S-INPT-COMMA1 PIC X(01).
05 S-INPT-CODE PIC X(07).
05 S-INPT-COMMA2 PIC X(01).
05 S-INPT-COUNT PIC 9(04).
05 S-INPT-CRLF PIC X(02).
* 売上明細(ソート後)
FD SALES-SORT.
01 S-SORT-RECORD PIC X(23).
* 売上明細(処理用)
SD SALES-TEMP.
01 S-TEMP-RECORD.
05 S-TEMP-DATE PIC X(08).
05 S-TEMP-COMMA1 PIC X(01).
05 S-TEMP-CODE PIC X(07).
05 S-TEMP-COMMA2 PIC X(01).
05 S-TEMP-COUNT PIC 9(04).
05 S-TEMP-CRLF PIC X(02).
*
FD MASTER-FILE.
01 MASTER-FILER.
05 MASTER-RECORD PIC X(44).
FD DETAIL-FILE.
01 DETAIL-FILER.
05 DETAIL-RECORD PIC X(23).
FD REPORT-FILE.
01 REPORT-FILER.
05 REPORT-RECORD PIC X(60).
*
WORKING-STORAGE SECTION.
01 WORK.
05 MASTER-KEY PIC X(07).
05 DETAIL-KEY PIC X(07).
*
01 MASTER-RECORD-WORK.
05 MASTER-SYOHIN-CODE PIC X(07).
05 MASTER-COMMA1 PIC X(01).
05 MASTER-SYOHIN-NAME PIC N(09).
05 MASTER-COMMA2 PIC X(01).
05 MASTER-SYOHIN-PRICE PIC 9(06).
05 MASTER-CRLF PIC X(02).
*
01 DETAIL-RECORD-WORK.
05 DETAIL-HANBAI-DATE PIC X(08).
05 DETAIL-COMMA2 PIC X(01).
05 DETAIL-SYOHIN-CODE PIC X(07).
05 DETAIL-COMMA1 PIC X(01).
05 DETAIL-HANBAI-KOSUU PIC 9(04).
05 DETAIL-CRLF PIC X(02).
*
01 REPORT-RECORD-WORK.
05 REPORT-SYOHIN-CODE PIC X(07).
05 REPORT-COMMA1 PIC X(01) VALUE ','.
05 REPORT-SYOHIN-NAME PIC N(09).
05 REPORT-COMMA2 PIC X(01) VALUE ','.
05 REPORT-SYOHIN-PRICE PIC 9(06).
05 REPORT-COMMA3 PIC X(01) VALUE ','.
05 REPORT-HANBAI-KOSUU PIC 9(04).
05 REPORT-COMMA4 PIC X(01) VALUE ','.
05 REPORT-HANBAI-TOTAL PIC 9(10).
05 REPORT-CRLF PIC X(02) VALUE X'0D0A'.
*
*----------------------------------------
PROCEDURE DIVISION.
*----------------------------------------
* 各種処理の呼び出し
PROGRAM-START.
PERFORM SORT-START THRU SORT-END.
PERFORM INIT-START THRU INIT-END.
PERFORM MAIN-START THRU MAIN-END
UNTIL MASTER-KEY = HIGH-VALUE
AND DETAIL-KEY = HIGH-VALUE.
PERFORM CLOSE-START THRU CLOSE-END.
STOP RUN.
PROGRAM-END.
*
* 商品コードでのソート処理
SORT-START.
SORT MASTER-TEMP
ASCENDING KEY M-TEMP-CODE
USING MASTER-INPT
GIVING MASTER-SORT.
*
SORT SALES-TEMP
ASCENDING KEY S-TEMP-CODE
USING SALES-INPT
GIVING SALES-SORT.
SORT-END.
*
* 初回の読み込み
INIT-START.
OPEN INPUT MASTER-FILE.
OPEN INPUT DETAIL-FILE.
OPEN OUTPUT REPORT-FILE.
PERFORM MASTER-READ-START THRU MASTER-READ-END.
PERFORM DETAIL-READ-START THRU DETAIL-READ-END.
INIT-END.
*
* 繰り返し処理
MAIN-START.
IF MASTER-KEY < DETAIL-KEY
PERFORM ONLY-MASTER-START THRU ONLY-MASTER-END
ELSE
IF MASTER-KEY = DETAIL-KEY
PERFORM MATCHED-START THRU MATCHED-END
ELSE
PERFORM NO-MATCHED-START THRU NO-MATCHED-END
END-IF
END-IF.
MAIN-END.
*
* 商品マスタ読み込み
MASTER-READ-START.
READ MASTER-FILE
AT END
MOVE HIGH-VALUE TO MASTER-KEY
GO TO MASTER-READ-END
END-READ.
MOVE MASTER-RECORD TO MASTER-RECORD-WORK.
MOVE MASTER-SYOHIN-CODE TO MASTER-KEY.
MASTER-READ-END.
*
* 売上明細読み込み
DETAIL-READ-START.
READ DETAIL-FILE
AT END
MOVE HIGH-VALUE TO DETAIL-KEY
GO TO DETAIL-READ-END
END-READ.
MOVE DETAIL-RECORD TO DETAIL-RECORD-WORK.
MOVE DETAIL-SYOHIN-CODE TO DETAIL-KEY.
DETAIL-READ-END.
*
* 商品マスタに対応する明細がない
ONLY-MASTER-START.
PERFORM MASTER-READ-START THRU MASTER-READ-END.
ONLY-MASTER-END.
*
* 商品マスタに対応する明細がある
MATCHED-START.
PERFORM UNTIL MASTER-KEY < DETAIL-KEY
MOVE MASTER-SYOHIN-CODE TO REPORT-SYOHIN-CODE
MOVE MASTER-SYOHIN-NAME TO REPORT-SYOHIN-NAME
MOVE MASTER-SYOHIN-PRICE TO REPORT-SYOHIN-PRICE
MOVE DETAIL-HANBAI-KOSUU TO REPORT-HANBAI-KOSUU
MULTIPLY DETAIL-HANBAI-KOSUU BY MASTER-SYOHIN-PRICE
GIVING REPORT-HANBAI-TOTAL
MOVE REPORT-RECORD-WORK TO REPORT-FILER
WRITE REPORT-FILER
PERFORM DETAIL-READ-START THRU DETAIL-READ-END
END-PERFORM.
PERFORM MASTER-READ-START THRU MASTER-READ-END.
MATCHED-END.
*
* 明細に対応する商品マスタがない
NO-MATCHED-START.
DISPLAY 'TRAN ONLY ' MASTER-KEY DETAIL-KEY.
PERFORM DETAIL-READ-START THRU DETAIL-READ-END.
NO-MATCHED-END.
*
* 終了処理
CLOSE-START.
CLOSE MASTER-FILE.
CLOSE DETAIL-FILE.
CLOSE REPORT-FILE.
CLOSE-END.
データファイルはこんな感じです。
0000001,チョコ詰め合わせ ,000300
0000002,きざみショウガ煎餅,000500
0000004,ほろほろクッキー ,000010
0000003,10色豆アラカルト,000300
20220102,0000004,0012
20220103,0000002,0005
20220104,0000001,0001
20220105,0000003,0021
20220106,0000002,0008
20220107,0000001,0030
20220108,0000004,0009
20220109,0000003,0005
- 漢字はUTF-8なので3バイト換算です
- 入力ファイル(MASTER.TXT,SALES.TXT)の行末はCRLF(0x0A0D)の2バイトで設計されています。
- ただし、ファイルの作成方法によっては、行の終端がLFのみとなる場合があり、その場合は結果が崩れてしまいます。
- なので、実行結果がおかしい場合は、hexdump ファイル名 で実ファイルの行末を確認して下さい。
- 例えばSALES.TXTの行末がCRLFではなくLFだけの場合は、item.cobの50,62,93行目のX(02)をX(01)にして、53、69行目のX(23)をX(22)に書き換えればよいと思います。
3.3 画面入出力の例
こちらは画面入出力のサンプルです。日本語は使えません。
プログラムの内容は入力された日付の指定月後の応当日を求めるプログラムです。
- 入力日付が日付として成立するかチェック
- うるう年簡略化のため入力年を1951年~2099年に限定
- 入力日が月末であれば、応当日も末日に設定(末末対応)
- 応当日が月の大小の関係で暦日でない場合は、応当日は月末に設定
IDENTIFICATION DIVISION.
PROGRAM-ID. MAIN.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WORK.
05 Y1 PIC 9(1).
05 Y2 PIC 9(1) VALUE 4.
05 Y3 PIC 9(4).
01 INPUTS.
05 INPUT-DATE.
10 INPUT-YEAR PIC 9(4).
10 INPUT-MONTH PIC 9(2).
10 INPUT-DAY PIC 9(2).
05 INPUT-PERIOD PIC 99.
05 INPUT-CMD PIC 9.
01 OUTPUTS.
05 OUTPUT-DATE.
10 OUTPUT-YEAR PIC 9(4).
10 OUTPUT-MONTH PIC 9(2).
10 OUTPUT-DAY PIC 9(2).
01 INDICATORS.
05 MSG PIC X(40) VALUE "***".
05 TMP PIC 9(1).
SCREEN SECTION.
01 DATA-ENTRY-SCREEN.
05 VALUE "Period Date Calculator" BLANK SCREEN
LINE 1 COL 10.
05 VALUE "Start(date)" LINE 3 COL 5.
05 DATE-INPUT LINE 3 COL 25
PIC 9(8) TO INPUT-DATE.
05 VALUE "Period(month)" LINE 4 COL 5.
05 PERIOD-INPUT LINE 4 COL 25
PIC 99 TO INPUT-PERIOD.
05 VALUE "1:Go 9:Exit" LINE 6 COL 5.
05 RESPONSE-INPUT LINE 6 COL 25
PIC 9 TO INPUT-CMD.
05 SYSTEM-MSG LINE 8 COL 5
PIC X(40) FROM MSG.
*
*----------------------------------------
PROCEDURE DIVISION.
*----------------------------------------
* 各種処理の呼び出し
PROGRAM-START.
PERFORM LOOP-START THRU LOOP-END
UNTIL INPUT-CMD = 9.
STOP RUN.
PROGRAM-END.
*
* ループ処理
LOOP-START.
DISPLAY DATA-ENTRY-SCREEN.
ACCEPT DATA-ENTRY-SCREEN.
* -------------------------------
* STEP1 入力年月の検査
IF INPUT-YEAR > 1950 AND INPUT-YEAR < 2100
THEN
IF INPUT-MONTH > 0 AND INPUT-MONTH < 13
THEN
IF INPUT-DAY > 0 AND INPUT-DAY < 32
THEN
MOVE "STEP1" TO MSG;
ELSE
MOVE "DAY ERROR!" TO MSG;
END-IF
ELSE
MOVE "MONTH ERROR!" TO MSG;
END-IF
ELSE
MOVE "YEAR ERROR! (1951 - 2099)" TO MSG;
END-IF.
* -------------------------------
* STEP2 入力日の検査(末日ならSETEP3へ)
IF MSG = "STEP1"
THEN
MOVE "STEP2" TO MSG;
* 2月の場合
IF INPUT-MONTH = 2
THEN
DIVIDE Y2 INTO INPUT-YEAR GIVING Y3 REMAINDER Y1;
IF Y1 = 0
THEN
IF INPUT-DAY > 29
THEN
MOVE "DAY ERROR!! -29" TO MSG;
END-IF
IF INPUT-DAY = 29
THEN
MOVE "STEP3" TO MSG;
END-IF
ELSE
IF INPUT-DAY > 28
THEN
MOVE "DAY ERROR!! -28" TO MSG;
END-IF
IF INPUT-DAY = 28
THEN
MOVE "STEP3" TO MSG;
END-IF
END-IF
END-IF
* 小の月、大の月
IF INPUT-MONTH = 4
OR INPUT-MONTH = 6
OR INPUT-MONTH = 9
OR INPUT-MONTH = 11
THEN
IF INPUT-DAY > 30
THEN
MOVE "DAY ERROR!! -30" TO MSG;
END-IF
IF INPUT-DAY = 30
THEN
MOVE "STEP3" TO MSG;
END-IF
ELSE
IF INPUT-DAY > 31
THEN
MOVE "DAY ERROR!! -31" TO MSG;
END-IF
IF INPUT-DAY = 31
THEN
MOVE "STEP3" TO MSG;
END-IF
END-IF
END-IF.
* -------------------------------
* STEP2 入力日の検査(末日ならSETEP3へ)
IF MSG = "STEP2"
THEN
IF INPUT-PERIOD > 0 AND INPUT-PERIOD < 24
THEN
* 応答する年月を算出
MOVE INPUT-YEAR TO OUTPUT-YEAR;
MOVE INPUT-DAY TO OUTPUT-DAY;
ADD INPUT-MONTH INPUT-PERIOD GIVING OUTPUT-MONTH;
IF OUTPUT-MONTH > 12
THEN
SUBTRACT 12 FROM OUTPUT-MONTH;
ADD 1 TO OUTPUT-YEAR;
END-IF
* 応答月が2月の場合
IF OUTPUT-MONTH = 2
THEN
DIVIDE Y2 INTO OUTPUT-YEAR
GIVING Y3 REMAINDER Y1;
IF Y1 = 0
THEN
IF OUTPUT-DAY > 29
THEN
MOVE 29 TO OUTPUT-DAY;
END-IF
ELSE
IF OUTPUT-DAY > 28
THEN
MOVE 28 TO OUTPUT-DAY;
END-IF
END-IF
END-IF
IF OUTPUT-MONTH = 4
OR OUTPUT-MONTH = 6
OR OUTPUT-MONTH = 9
OR OUTPUT-MONTH = 11
THEN
* 応答月が小の月の場合
IF OUTPUT-DAY > 30
THEN
MOVE 30 TO OUTPUT-DAY;
END-IF
ELSE
* 応答月が大の月の場合
IF INPUT-DAY > 31
THEN
MOVE 31 TO OUTPUT-DAY;
END-IF
END-IF
* 応答日を回答として設定
MOVE OUTPUT-DATE TO MSG;
ELSE
MOVE "PERIOD ERROR!! 1-23" TO MSG;
END-IF
END-IF.
*
* STEP3 末末応当日
IF MSG = "STEP3"
THEN
* 応答する年月を算出
MOVE INPUT-YEAR TO OUTPUT-YEAR;
MOVE INPUT-DAY TO OUTPUT-DAY;
ADD INPUT-MONTH INPUT-PERIOD GIVING OUTPUT-MONTH;
* 応答月が2月の場合
IF OUTPUT-MONTH = 2
THEN
DIVIDE Y2 INTO OUTPUT-YEAR
GIVING Y3 REMAINDER Y1;
IF Y1 = 0
THEN
MOVE 29 TO OUTPUT-DAY;
ELSE
MOVE 28 TO OUTPUT-DAY;
END-IF
END-IF
IF OUTPUT-MONTH = 4
OR OUTPUT-MONTH = 6
OR OUTPUT-MONTH = 9
OR OUTPUT-MONTH = 11
THEN
* 応答月が小の月の場合
MOVE 30 TO OUTPUT-DAY;
ELSE
* 応答月が大の月の場合
MOVE 31 TO OUTPUT-DAY;
END-IF
* 応答日を回答として設定
MOVE OUTPUT-DATE TO MSG;
END-IF.
LOOP-END.
まぁなんて保守性のないソースでしょうか・・・。やっつけ感が満載ですが、とりあえず動いたのでサンプルとして載せました。
付録 Herculesのご紹介
こちらは記事のご紹介です。手順通り実施するとWindows上で仮想MVSが動作します。JCLが動きますので、そういった部分の自宅学習にはよいのではないでしょうか。
最後に
いかがだったでしょうか。恥ずかしながら初めてCOBOLのプログラムを書いてみました。なので書き方がイマイチなのだとは思うのですが、プログラムをシンプルに書けるのは良いなと思いました。
DISPLAY LINE COL で日本語が使えないとか、REPORT SECTIONがサポートされていないとか、構築環境がイマイチなのかもしれません。OpenSourceさんからはDocker版が提供されているので、Dockerを利用できる人はそちらを使って学習するのが最も良い気がします。
次回こそ・・・
IPFSを使ったNFTに挑戦したいと思います。
記事一覧