1人COBOL再研修〜データ入出力編〜

[これ](https://qiita.com/zama_8722/items/adf22305cbd9033dc87e)の続き。先日いよいよ配属が決まり、研修が終わればバリバリCOBOLを使うことになりそうだ…。

話を戻して、今回は外部ファイルを取り扱ってみようと思う。さぁ、頑張ろう。

## 外部ファイルを定義する

外部ファイルを開く前に、環境部とデータ部に追記が必要。

### 環境部

1. 新たに入出力節`INPUT-OUTPUT SECTION.`を追加する
2. 入出力節の中に、ファイル管理記述`FILE-CONTROL.`を追加する
3. 構文で使用するファイル名と実際のファイルを対応付ける
	
```:構文
SELECT <ファイル名> ASSIGN <実際のファイル>
        　　　　　 ORGANIZATION IS LINE SEQUENTIAL.
```
	
* `<ファイル名>`は任意の名前（変数みたいなもの）
* 実際のファイルはそのファイルをディレクトリを書く
* `ORGANIZATION IS LINE SEQUENTIAL`をつけることで改行区切りになる。これをつけないと環境によっては改行区切りができない。

ここまでまとめるとこうなる。

```:環境部まで
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
       INPUT-OUTPUT           SECTION.
       FILE-CONTROL.
         SELECT  IN-FL        ASSIGN  "infile.dat"
　　　　　　　　　　　　　　　　　　ORGANIZATION IS LINE SEQUENTIAL.
         SELECT  OUT-FL       ASSIGN  "outfile.dat".
```

今回infile.datの中身は以下のようにする。

```:infile.dat
HELLOWORLD
SAMPLE PGM
```

outfile.datは空データである。

### データ部

1. 新たにファイル節`FILE SECTION.`を追加する
2. ファイル節の中にファイルを定義する
3. 2.の直下にファイルのレコード情報を記述する
	* 記述方法はデータ項目定義と同じ
4. [`WORKING-STORAGE SECTION.`にデータの受取先を作る。PICTURE句で指定するサイズの合計は3.で指定したものと同じになるようにする]
	* これは任意で、作らなかった場合でも3.で定義したデータ項目から参照できる
5. `WORKING-STORAGE SECTION.`には、PICTURE句`X(01)`のフラグ用データ項目も作っておく（理由は後述）

```:構文
FD <ファイル名> [LABEL RECORD STANDARD
 　　　　 BLOCK CONTAINS n RECORDS].
```
 * `[]`内は任意（詳細は[後述](#後述としていた任意箇所について)）
  * 記載なしの場合は`LABEL RECORD STANDARD`とみなす
 * `<ファイル名>`は環境部で書いた任意のファイル名

ここまでをまとめるとこんな感じ。

```:データ部まで
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
       INPUT-OUTPUT           SECTION.
       FILE-CONTROL.
         SELECT  IN-FL        ASSIGN  "infile.dat"
　　　　　　　　　　　　　　　　　　ORGANIZATION IS LINE SEQUENTIAL.
         SELECT  OUT-FL       ASSIGN  "outfile.dat".
      *
       DATA                   DIVISION.
       FILE                   SECTION.
       FD  IN-FL              LABEL RECORDS STANDARD
                              BLOCK CONTAINS 0 RECORDS.
       01  IN-REC             PIC X(10).
       FD  OUT-FL             LABEL RECORD OMITTED.
       01  OUT-REC            PIC X(10).
       WORKING-STORAGE        SECTION.
       01  IN-DATA.
         03  IN-STR           PIC X(10).
       01  OUT-DATA.
         03  OUT-STR          PIC X(10).
```

#### 後述としていた任意箇所について

__`LABEL RECORD STANDARD`__

ファイルのラベルの有無を示す。ラベルがあるなら`STANDARD`、ないなら`OMITTED`。読み込みのデータは`STANDARD`、帳票出力は`OMITTED`使ってねと言われた。

__`BLOCK CONTAINS n RECORDS`__

物理レコードのサイズを指定する。`n`には数値が入る。`0`にしておけばユーザーに代わってシステムが決定する。

## 外部ファイルを開く

外部ファイルを開くには、`OPEN`命令を用いる。

```
OPEN  <INPUT/OUTPUT>  <ファイル名>.
```
読み込みファイルのときには`INPUT`、書き込みファイルのときは`OUTPUT`とする。また、`<ファイル名>`は環境部で書いた任意のファイル名とする。

## 外部ファイルを読み書きする

### 外部ファイルを読み込む

ファイルを読み込む際は`READ`命令を用いる。この命令を使用する際には、外部ファイルを開くときに`INPUT`としなければいけない。

```:構文
READ  <ファイル名>  [INTO  <データ名>].
  AT END
    <EOF時の処理>
  NOT AT END
    <EOFでないときの処理>
END-READ.
```

`[]`内の箇所は[データ部の4.](#データ部)を定義した際に使用する。

ファイルが終了したことは、`AT END`以外では分からない。なので、先程定義するようにと記載したフラグを使用して、全体でファイルが終了したことを分かるようにする。

### 外部ファイルに書き込む

ファイルに書き込む際は`WRITE`命令を用いる。この命令を使用する際には、外部ファイルを開くときに`OUTPUT`としなければいけない。

```:構文
WRITE  <レコード名>  [FROM  <データ名> [AFTER  XXXX]].
```

`FROM <データ名>`は、ただ単に`MOVE <データ名> TO <レコード名>`をしているだけなので、別の箇所でうまくやりくりしているのならいらない。

#### 任意の`[AFTER XXXX]`について

帳票出力とかする際に、改行数を決定する。`XXXX`は2つ書き方がある。

|書き方|意味|
|:---:|:---|
|`1`のような数値|改行する回数|
|`PAGE`|改ページ|

例えば`...AFTER PAGE 2.`とすると、1行おきにデータが出力される。

## ファイルを閉じる

開いたら最後はもちろん閉じる。

```:構文
CLOSE  <ファイル名>.
```

`<ファイル名>`は環境部で書いた任意のファイル名とする。

## サンプルプログラム

間が空いてしまったが、ここまでをまとめる。ここではinfile.datの中身をoutfile.datに書く。ここではoutfile.datは帳票であると想定する。

```:サンプル
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
       INPUT-OUTPUT           SECTION.
       FILE-CONTROL.
         SELECT  IN-FL        ASSIGN TO 'infile.dat'
                              ORGANIZATION IS LINE SEQUENTIAL.
         SELECT  OUT-FL       ASSIGN TO 'outfile.dat'.
      *
       DATA                   DIVISION.
       FILE                   SECTION.
       FD  IN-FL              LABEL RECORD STANDARD
                              BLOCK CONTAINS 0 RECORDS.
       01  IN-REC             PIC X(10).
       FD  OUT-FL             LABEL RECORD OMITTED.
       01  OUT-REC            PIC X(10).
       WORKING-STORAGE        SECTION.
       01  IN-DATA.
         03  IN-STR           PIC X(10).
       01  FLG-EOF            PIC X(01).
      *
       PROCEDURE              DIVISION.
      *-- ﾌﾟﾛｸﾞﾗﾑﾉﾌﾛｰ
       PGM-FLOW               SECTION.
       PGM-FLOW-S.
         PERFORM              PGM-OPEN.
         PERFORM              PGM-RW
           UNTIL              FLG-EOF = '1'.
         PERFORM              PGM-CLOSE.
       PGM-FLOW-E.
         STOP RUN.
      *-- ﾌｧｲﾙOPEN AND ｼｮｶｲﾌｧｲﾙREAD
       PGM-OPEN               SECTION.
       PGM-OPEN-S.
         OPEN                 INPUT  IN-FL
                              OUTPUT OUT-FL.
         MOVE                 SPACE  TO  FLG-EOF.
         PERFORM              PGM-READ.
       PGM-OPEN-E.
         EXIT.
      *-- ﾌｧｲﾙREAD
       PGM-READ               SECTION.
       PGM-READ-S.
         READ                 IN-FL  INTO  IN-DATA
           AT END
             MOVE             '1'  TO  FLG-EOF
         END-READ.
       PGM-READ-E.
         EXIT.
      *-- ﾌｧｲﾙWRITE AND ﾂｷﾞﾉﾌｧｲﾙREAD
       PGM-RW                 SECTION.
       PGM-RW-S.
         WRITE                OUT-REC FROM IN-DATA AFTER 1.
         PERFORM              PGM-READ.
       PGM-RW-E.
         EXIT.
      *-- ﾌｧｲﾙCLOSE
       PGM-CLOSE              SECTION.
       PGM-CLOSE-S.
         CLOSE                IN-FL
                              OUT-FL.
       PGM-CLOSE-E.
         EXIT.
```

```:結果（outfile.dat）

HELLOWORLD
SAMPLE PGM
```

最初の1行が空いているが、`AFTER 1`としているからでなので問題ない。不要ならば行数をカウントしておいて、最初の行だけ改行しないよう`IF`で管理してあげればよい。

## 今回はここまで

ちょっと急ぎ足でまとめたので後日見直して、間違いを見つけたら修正しよう。

研修では`ORGANIZATION LINE SEQUENTIAL`をつけずにうまく行っていたので、いざ家でやったときにうまく行かなかったときは結構悩んだ。逆に言うといい勉強にはなった。

うまくいこうがいかまいが、`ORGANIZATION LINE SEQUENTIAL`にすれば改行区切りになるので、つけておいたほうがいいかもれない。

次回はテーブルの再定義。おそらく次が最終回になりそう。