技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

opensource COBOL:内部ソートサンプル

COBOLの内部ソート(COBOLプログラム内で行うソート処理)についてネット上の情報が少ないので、opensource COBOLでサンプルを作成しました。
また、サンプルを元に、文法に関する簡単な説明もつけました。
 
【サンプル】
       IDENTIFICATION    DIVISION.
       PROGRAM-ID.       SORTTEST.
       ENVIRONMENT       DIVISION.
       INPUT-OUTPUT      SECTION.
       FILE-CONTROL.   
           SELECT  F1  ASSIGN  TO  "C:\tmp\a.txt".
           SELECT  F2  ASSIGN  TO  "C:\tmp\b.txt".
           SELECT  FS  ASSIGN  TO  "C:\tmp\s.txt".
      *
       DATA                  DIVISION.
       FILE                  SECTION.
       FD  F1.
       01  F1R.
           03  F1-REC        PIC X(07).
       FD  F2.
       01  F2R.
           03  F2-REC        PIC X(07).
       SD  FS.
       01  FSR.
           03  FS-KEY        PIC X(01).
           03  FS-REC-DATA   PIC X(06).
      *
       WORKING-STORAGE       SECTION.
       01  WORK.
           03  F1-END        PIC X(01).
           03  FS-END        PIC X(01).
      *
       PROCEDURE         DIVISION.
      *
      * 各種処理の呼び出し
      *
       CONTROLLER-S.
           INITIALIZE WORK.
           SORT FS ON ASCENDING FS-KEY
                INPUT  PROCEDURE SORTIN-START  THRU SORTIN-END
                OUTPUT PROCEDURE SORTOUT-START THRU SORTOUT-END.
           STOP RUN.
       CONTROLLER-E.
      *
      * ソート前処理
      *
       SORTIN-START.
           OPEN  INPUT  F1.
           PERFORM READRELEASE-S THRU READRELEASE-E
               UNTIL F1-END = HIGH-VALUE.
           CLOSE  F1.
       SORTIN-END.
      *
      * インプットファイルREAD・ソートファイルRELEASE
      *
       READRELEASE-S.
           READ F1
           AT END
               MOVE HIGH-VALUE TO F1-END
               GO TO READRELEASE-E
           END-READ.
           MOVE F1R TO FSR.
           RELEASE FSR.
       READRELEASE-E.
      *
      * ソート前処理
      *
       SORTOUT-START.
           OPEN  OUTPUT F2.
           PERFORM RETURNWRITE-S THRU RETURNWRITE-E
               UNTIL FS-END = HIGH-VALUE.
           CLOSE  F2.
       SORTOUT-END.
      *
      * ソートファイルRETURN・アウトプットファイルWRITE
      *
       RETURNWRITE-S.
           RETURN FS
           AT END
               MOVE HIGH-VALUE TO FS-END
               GO TO RETURNWRITE-E
           END-RETURN.
           MOVE FSR TO F2R.
           WRITE F2R.
       RETURNWRITE-E.

【入力ファイル】
・a.txt
2fuga
3piyo
1hoge

【出力ファイル】
・b.txt
1hoge
2fuga
3piyo

【文法に関する補足】
・ASSIGN文でソート用の一時ファイルを定義する必要がある。
・SORT文の「ON 条件 ワーク名」でソートキーを指定することができる。条件については、「ASCENDING」なら昇順、「DESCENDING」なら降順となる。「条件 ワーク名」を繰り返して複数のキーを指定することもできる。
・SORT文はソート前に「INPUT  PROCEDURE」で定義したプロシージャを実行する。ここでは、RELEASE文でソート用の一時ファイルにレコードを書き込む。
・SORT文はソート後に「OUTPUT PROCEDURE」で定義したプロシージャを実行する。ここでは、ソート用の一時ファイルからRETURN文でレコードを読みだす。
(「INPUT  PROCEDURE」が終了後、「OUTPUT PROCEDURE」の開始前に、ソート用の一時ファイルに書き込まれたレコードはソートされる)

DOSのfindstrで日本語が検索できない件(文字コードが原因)

実務で表題の問題にぶち当たって焦ったのですが、検索対象のファイルがSJISではなかった(UTF-8であった)ことが原因でした。
 
【対処】
検索前に下記リンク先のPowerShellスクリプトを呼び出し、検索対象のファイルの文字コードSJISに変更する。
(フォルダ構成や文字コードに合わせて、変数定義の修正若しくは引数の指定が必要)
 
WindowsPowerShellで複数のファイルの文字コードを一括変換する:Tech TIPS - @IT

http://www.atmarkit.co.jp/ait/articles/0804/18/news155.html
 
PowerShellを実行して「このシステムではスクリプトの実行が無効になっているため…」と出たら、呼び出すコマンドを
powershell -ExecutionPolicy RemoteSigned .\FileConvert.ps1
にする。
 
PowerShell スクリプトの実行が無効となっている場合の対処法 - カタカタブログ

http://totech.hateblo.jp/entry/2017/09/29/162411

COBOLの練習用プログラムの紹介

COBOLを勉強すると、恐らくひよこグミ様の「サルでもわかるCOBOL入門」というサイトを見つけると思います。
そのサイトにCOBOLの練習用プログラムがあるので紹介します。
 
サルでもわかるCOBOL入門 【ひよこグミ】 - はじめてのCOBOL 【総合メニュー】

http://www16.plala.or.jp/hiyokogumi/3/300.html
 
私の環境(Windowsのopensource COBOL)で何本か動作確認しましたが、上記ページで紹介されているプログラムは概ね動作すると思います。
私が発見した問題点は以下で、何れも学習に影響のないものです。
・ファイルに「SELECT  HOGE  ASSIGN  TO  PRINTER」を指定してWRITEしても何も出力されない
 ※私のPCにプリンターを繋いでないとか、そういう系の問題かもしれません。
  何れにしても普通にファイルに出力すれば問題なく学習できます。
・プログラムにケアレスミスがあるかもしれない
 ※Mission4-1のプログラムの30行目にコメントを示す*がありませんでした。
  *をつけたら問題なく動作しました。
 
今回紹介したページではテーブル処理(配列処理)のサンプルもあり、基本情報処理技術者試験の午後選択問題で頻出であり、実務でも使用頻度は高いです。
 
今回紹介したページで用意されていない解説・サンプルは、私の方で用意しました。

コントロールブレイク処理とマッチング処理は基本情報処理技術者試験の午後選択問題でも実務でも頻出です。

内部ソート処理は基本情報処理技術者試験の午後選択問題で頻出です。実務では使わない現場もありますが、私は実務でも使用経験があります。

データベース連携は基本情報処理技術者試験の午後選択問題では(恐らく)出題されませんが、午前問題で出題されることがあるので、COBOLを使わない人にとってはある意味一番重要な文法です。実務では頻出で、COBOLを使わない人でも「カーソル」「FETCH」といった用語は知っておくべきです。
 
・コントロールブレイク処理

https://akira2kun.hatenablog.com/entry/2018/10/08/135924
 
・マッチング処理

https://akira2kun.hatenablog.com/entry/2018/10/13/221833

 

・内部ソート

https://akira2kun.hatenablog.com/entry/2018/10/20/161614

 

・データベース連携(解説のみ)

https://akira2kun.hatenablog.com/entry/2018/10/20/183304

---------------------
情報処理技術者試験対策 目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

opensource COBOL:マッチング処理サンプル

マッチング処理(マスタとトランザクションを突き合わせる処理)のロジックについて、以前の記事で紹介しました。

https://akira2kun.hatenablog.com/entry/2018/10/13/155427
 
COBOLでサンプルプログラムを作成しましたので、参考までに紹介します。
 
【サンプルプログラム】
       IDENTIFICATION    DIVISION.
       PROGRAM-ID.       MATCHING.
       ENVIRONMENT       DIVISION.
       INPUT-OUTPUT      SECTION.
       FILE-CONTROL.   
           SELECT  F1  ASSIGN  TO  "C:\tmp\a.txt".
           SELECT  F2  ASSIGN  TO  "C:\tmp\b.txt".
           SELECT  F3  ASSIGN  TO  "C:\tmp\c.txt".
      *
       DATA                  DIVISION.
       FILE                  SECTION.
       FD  F1.
       01  F1R.
           03  F1-REC        PIC X(30).
       FD  F2.
       01  F2R.
           03  F2-REC        PIC X(33).
       FD  F3.
       01  F3R.
           03  F3-REC        PIC X(31).
      *
       WORKING-STORAGE       SECTION.
       01  WORK.
           03  F1-KEY        PIC X(07).
           03  F2-KEY        PIC X(07).
       01  F1-REC-WORK.
           03  F1-SYOHIN-CODE
                             PIC X(07).
           03  F1-COMMA1     PIC X(01).
           03  F1-SYOHIN-NAME
                             PIC X(20).
           03  F1-CRLF       PIC X(02).
       01  F2-REC-WORK.
           03  F2-SYOHIN-CODE
                             PIC X(07).
           03  F2-COMMA1     PIC X(01).
           03  F2-HANBAI-YMD PIC X(08).
           03  F2-COMMA2     PIC X(01).
           03  F2-HANBAI-KOSUU
                             PIC X(05).
           03  F2-COMMA3     PIC X(01).
           03  F2-HANBAI-KINGAKU
                             PIC X(09).
           03  F2-CRLF       PIC X(02).
       01  F3-REC-WORK.
           03  F3-SYOHIN-NAME
                             PIC X(20).
           03  F3-COMMA1     PIC X(01) VALUE ','.
           03  F3-HANBAI-YMD PIC X(08).
           03  F3-CRLF       PIC X(02) VALUE X'0D0A'.
      *
       PROCEDURE         DIVISION.
      *
      * 各種処理の呼び出し
      *
       000-CONTROLLER-S.
           PERFORM  100-START-S THRU 100-START-E.
           PERFORM  200-MAIN-S  THRU 200-MAIN-E
               UNTIL F1-KEY = HIGH-VALUE
               AND   F2-KEY = HIGH-VALUE.
           PERFORM  300-END-S   THRU 300-END-E.
           STOP RUN.
       000-CONTROLLER-E.
      *
      * 前処理
      *
       100-START-S.
           OPEN  INPUT  F1.
           OPEN  INPUT  F2.
           OPEN  OUTPUT F3.
           PERFORM 210-MASTER-READ-S THRU 210-MASTER-READ-E.
           PERFORM 220-TRAN-READ-S   THRU 220-TRAN-READ-E.
       100-START-E.
      *
      * 主処理
      *
       200-MAIN-S.
           IF     F1-KEY < F2-KEY
                  PERFORM 230-MASTER-ONLY-S THRU 230-MASTER-ONLY-E
           ELSE
               IF F1-KEY = F2-KEY
                  PERFORM 240-MATCH-S       THRU 240-MATCH-E
               ELSE
                  PERFORM 250-TRAN-ONLY-S   THRU 250-TRAN-ONLY-E
               END-IF
           END-IF.
       200-MAIN-E.
      *
      * READ処理(マスタファイル
      *
       210-MASTER-READ-S.
           READ F1
           AT END
               MOVE HIGH-VALUE TO F1-KEY
               GO TO 210-MASTER-READ-E
           END-READ.
           MOVE F1-REC         TO F1-REC-WORK.
           MOVE F1-SYOHIN-CODE TO F1-KEY.
       210-MASTER-READ-E.
      *
      * READ処理(トランザクションファイル
      *
       220-TRAN-READ-S.
           READ F2
           AT END
               MOVE HIGH-VALUE TO F2-KEY
               GO TO 220-TRAN-READ-E
           END-READ.
           MOVE F2-REC         TO F2-REC-WORK.
           MOVE F2-SYOHIN-CODE TO F2-KEY.
       220-TRAN-READ-E.
      *
      * マスタのみ処理
      *
       230-MASTER-ONLY-S.
           PERFORM 210-MASTER-READ-S THRU 210-MASTER-READ-E.
       230-MASTER-ONLY-E.
      *
      * マッチ時処理
      *
       240-MATCH-S.
           PERFORM UNTIL F1-KEY < F2-KEY
               MOVE    F1-SYOHIN-NAME  TO F3-SYOHIN-NAME
               MOVE    F2-HANBAI-YMD   TO F3-HANBAI-YMD
               MOVE    F3-REC-WORK     TO F3-REC
               WRITE   F3R
               PERFORM 220-TRAN-READ-S THRU 220-TRAN-READ-E
           END-PERFORM.
           PERFORM 210-MASTER-READ-S THRU 210-MASTER-READ-E.
       240-MATCH-E.
      *
      * トランザクションのみ処理
      *
       250-TRAN-ONLY-S.
           DISPLAY 'TRAN ONLY ' F2-KEY.
           PERFORM 220-TRAN-READ-S   THRU 220-TRAN-READ-E.
       250-TRAN-ONLY-E.
      *
      * 後処理
      *
       300-END-S.
           CLOSE  F1.
           CLOSE  F2.
           CLOSE  F3.
       300-END-E.
 
マスタファイル
・C:\tmp\a.txt
0000001,hoge               
0000002,fuga               
0000004,piyo               
 
トランザクションファイル
・C:\tmp\b.txt
0000001,20180401,00100,00010000
0000001,20180402,00200,00020000
0000003,20180401,00001,00001000
0000004,20180401,00002,00002000
 
【出力ファイル】
・C:\tmp\c.txt
hoge                ,20180401
hoge                ,20180402
piyo                ,20180401
 
【標準出力】
TRAN ONLY 0000003

マッチング処理のロジックの説明

今回は、バッチプログラムで使われるロジックの一つである「マッチング処理」について説明します。
「マッチング処理」とは特にCOBOLではよく目にするロジックであり、マスタデータ(業務の基盤となるデータ。商品一覧、取引先一覧等。)とトランザクションデータ(業務で日々発生するデータ。販売履歴、入金履歴等。)を突き合わせる処理です。
マッチング処理を覚えておけば、実業務でバッチプログラムを作る時のヒントになりますし、開発者同士のコミュニケーションもスムーズになります。
 
突き合わせを行う時は、マスタデータとトランザクションデータで同じキー項目(商品コード、取引先コード等)を使用し、そのキー項目で昇順にソートした後、マスタデータとトランザクションデータを1件ずつ読み、キー項目が一致するかどうかで突き合わせを行います。
マスタデータとトランザクションデータでキー項目が一致した場合(マスタで管理しているもので取引が発生した)と、マスタのみキー項目が存在している場合(マスタで管理しているが取引は発生しなかった)は正常ケースですが、トランザクションのみキー項目が存在している場合(マスタで管理していないものが取引された)は異常ケースとなります。
 
マスタデータの一つのキー項目に対して、トランザクションデータの0~1つのレコードが対応する場合は、1対1マッチングと呼ばれます。2つ以上のレコードが対応することがある場合は1対nマッチングと呼ばれ、処理が少しだけ複雑になります。
 
フローチャートと例は以下の通りとなります。
 
フローチャート

f:id:akira2kun:20181013155231j:plain 
【例】
・要件
商品名が管理されている商品マスタと、商品の販売履歴(トランザクション)をファイル形式で読み込み、商品名と販売日を別ファイルで出力したい。
 
・商品マスタのフォーマット
カンマ区切りの固定長ファイル。
商品コードでレコードを一意に特定できるようにデータをセットする。
 
商品コード(7桁)
カンマ(1桁)
商品名(20桁)
 
・販売履歴のフォーマット
カンマ区切りの固定長ファイル。
商品コード・販売日でレコードを一意に特定できるようにデータをセットする。
 
商品コード(7桁)
カンマ(1桁)
販売日(8桁)
カンマ(1桁)
販売個数(5桁)
カンマ(1桁)
販売金額(9桁)
 
・出力ファイルのフォーマット
商品名(20桁)
カンマ(1桁)
販売日(8桁)
 
・商品マスタのレコード
0000001,hoge               
0000002,fuga               
0000004,piyo               
 
・販売履歴のレコード
0000001,20180401,00100,00010000
0000001,20180402,00200,00020000
0000003,20180401,00001,00001000
0000004,20180401,00002,00002000
 
・処理の流れ
 ①商品マスタ1レコード目と販売履歴1レコード目を読みこむ
 ②キー項目がマスタ(0000001)=トラン(0000001)のため、マッチ時処理実行
  出力ファイルを1レコード出力する
 ③販売履歴2レコード目を読みこむ
 ④キー項目がマスタ(0000001)=トラン(0000001)のため、マッチ時処理続行
  出力ファイルを1レコード出力する
 ⑤販売履歴3レコード目を読みこむ
 ⑥キー項目がマスタ(0000001)<トラン(0000003)のため、マッチ時処理終了
 ⑦商品マスタ2レコード目を読みこむ
 ⑧キー項目がマスタ(0000002)<トラン(0000003)のため、マスタのみ処理実行
  何もしない
 ⑨商品マスタ3レコード目を読みこむ
 ⑩キー項目がマスタ(0000004)>トラン(0000003)のため、トランのみ処理実行
  エラー出力
 ⑪販売履歴4レコード目を読みこむ
 ⑫キー項目がマスタ(0000004)=トラン(0000004)のため、マッチ時処理実行
  出力ファイルを1レコード出力する
 ⑬販売履歴EOFを読み込む
 ⑭キー項目がマスタ(0000004)<トラン(最大値)のため、マッチ時処理終了
 ⑮商品マスタEOFを読みこむ
 ⑯マスタもトランもEOFになったため処理終了
 
・出力ファイル
hoge                ,20180401
hoge                ,20180402
piyo                ,20180401

Windowsバッチの文字列置換(置換文字列を変数で定義)

備忘録です。
遅延環境変数を使ったり、環境変数のネストを使ったりして色々複雑なので。
 
【バッチサンプル】
・test.bat
rem 空ファイル作成
type nul> output.txt

rem 置換前文字列・置換後文字列を変数で定義
set before=hoge
set after=piyo

rem before・afterはfor文実行前に展開する(%で変数定義)
rem inputファイルの行(line)はfor文実行中に展開する(!で変数定義)
setlocal enabledelayedexpansion
for /f "delims=" %%I in (input.txt) do (
    set line=%%I
    echo !!line:%before%=%after%!!>> output.txt
)
endlocal

 

【入力ファイル例】
・input.txt
hogefuga
hoge

 

【出力ファイル例】
・output.txt
piyofuga
piyo

ドキュメント作成時の基本的な心構え

業務内容によってはドキュメントを作成する機会がなかなかないと思うので、ドキュメント作成のコツについて一般的なことを書いてみます。
ドキュメント作成にコツも何もあるのか、って感じかもしれませんが、ドキュメントの良し悪しで意図が早く正確に伝わるかどうかが変わってくるので、スムーズに仕事をこなすには欠かせないスキルです。
ドキュメントの質が悪い場合、わかりにくいと言われて怒られるだけならまだ良い方で、最悪の場合は情報の受け手の誤解によりシステム障害に繋がることすらあります。
 
ドキュメントには設計書からユーザ向けのマニュアル、運用手順等、色々種類がありますが、今回はどのようなドキュメントについても通じることを書きます。
 
【根→葉の流れで構造的に書く】
小説のように文章をつらつらと書くのではなく、適度に見出しを付けることが重要です。
また、その見出しは、根→葉の流れで構造的に書くことが重要で、見出しだけを見れば何がどこに書いてあるのかがわかるようにすることが理想です。
プログラム改修の設計書で言うと、以下のように書くのが良いでしょう。
 
 1.背景
  ……
 2.リリース日
  ……
 3.修正箇所
  3.1.プログラムA
   3.1.1.hoge対応
    ……
   3.1.2.fuga対応
    ……
  3.2.プログラムB
   ……
  3.3.プログラムC
   ……
 
【箇条書きや表や図で完結に書く】
・箇条書きの使用
 並列である複数の事柄を書く時は、箇条書きを用いると文章が見やすくなります。
 例えば、
 「条件Aの時か、条件Bの時か、条件Cの場合に、hoge処理をする」
 という文章は、以下のように書くとわかりやすくなります。
 
 以下の何れかの条件に当てはまる時に、hoge処理をする。
  ・条件A
  ・条件B
  ・条件C
 
・表の使用
 2×2かそれ以上の項目・条件が絡み合う事象を書く場合は、
 表を用いると状況をわかりやすく整理できます。
 
 例えば、下記はリスクの対応策を表にまとめたものです。 

https://cdn-ak.f.st-hatena.com/images/fotolife/a/akira2kun/20180709/20180709232641.jpg

 
 これを表を使わずに表現すると、
 「脅威に対しては回避と転嫁と軽減の戦略があり、回避とは…」
 といういかにも冗長な文章になってしまいます。
 
・図の使用
 複雑な事象を言葉で説明するのは難しいですが、
 図解するとわかりやすく説明できることが多いです。
 
 例えば、理解が難しい概念である
 「モジュール強度」と「モジュール結合度」
 を図解したものが以下です。
 https://akira2kun.hatenablog.com/entry/2018/07/07/144209
 
 日本語を読んでも何が言いたいのかわかりにくいと思いますが、
 図解することで意図を理解しやすくなると思います。
 
【誤解のない表現を使う】
・客観的な数値を示す
 「性能が大幅に良くなった」「キャパシティに与える影響は軽微である」
 といった主観的な表現だと、
 それぞれの受け手毎に異なる解釈をされる可能性があります。
 
 ここは、異なる解釈をされないように、
 「性能が10倍になった」「ディスク容量が100GBであるがデータ増加量は10MBである」
 といった形で、客観的な数字で表すことが重要です。
 
・複数の意味で取られる日本語を避ける
 すみません、すぐに良い例が思い浮かばないので引用しますが…
 
 日本語の難しさ:文法編(読点一つで意味が変わる?!):ウェブ・コト
 https://www.webkoto555.jp/column/?id=1514429008-408625

 
 「私は何度も咳をする彼を見ました」
 という文章が
 「咳をする彼」を何度も見た のか、
 「何度も咳をする彼」を見た のか、よくわからん!
 …ということが設計書や手順書を書く中でも起こり得ます。
 
 出来上がった文章を読み返して、
 複数の意味に取られないか考える癖を身に付けることが重要です。
 複数の意味に取られかねない時は、上手く言い換える必要があります。
 
・要件、仕様等を明確にする
 要件定義書なら誰がいつどのようにシステムを使うのか定義する必要がありますし、
 画面設計書なら画面項目のフォーマットや桁数等を定義する必要があります。
 
 ドキュメントによって絶対に定義しなければならないポイントがあるので、
 そのポイントを落とさないようにすることが重要です。
 もし定義が不十分だと、
 情報の受け手に自分の意図とは異なる解釈をされてしまう恐れがあります。
 
【情報の受け手に合わせて表現を変える】
システムの利用者にとっては、
システムをどのように操作すれば良いのかがわかれば良いので、
詳しい仕様は冗長な情報になります。
逆に、システムの開発者にとっては詳しい仕様こそが重要になります。
 
このように、情報の受け手によって、どのような情報を求めているかが異なります。
ドキュメントを作成する際は、情報の受け手が誰であるかを想像して、
適切な粒度で情報を提供することが重要になります。