1人COBOL再研修〜データ項目のあれこれ編〜

[これ](https://qiita.com/zama_8722/items/90febb1e27c73aa20809)の続き。更新頻度が遅すぎてCOBOL研修が終わってしまったが、変わらずマイペースで進めていこう。

今回はデータ項目周りをやるとのことで、COBOL構文というより、データの型とかそっちの話になる。

## おさらい「データ項目の定義方法」

[ここ](https://qiita.com/zama_8722/items/65935680e157de2c7927#%E3%81%9D%E3%81%AE%E5%89%8D%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E9%A0%85%E7%9B%AE%E3%82%92%E4%BD%9C%E3%82%8B%E3%81%AB%E3%81%AF)でやった。

```:構文
レベル番号  項目名            PIC 型(文字数).
```

を`WORKING-STORAGE SECTION.`に書くのだった。例えば、5文字入る文字列をレベル1で定義するときは

```
01  DT-NAME            PIC X(05).
```

で良い。今回はこの構文周りについて細かく見ていく。

## レベル番号

レベル番号を指定することで、複数のデータ項目を1つのグループにして扱えるようになったりする。例えば、

```:サンプル
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  DT-PARENT.
         03  DT-CHILD1        PIC X(05).
         03  DT-CHILD2        PIC X(05).
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
           MOVE               'HELLO'  TO  DT-CHILD1.
           MOVE               'WORLD'  TO  DT-CHILD2.
           DISPLAY            DT-PARENT.
           STOP RUN.
```

とすると

```:実行結果
HELLOWORLD
```

となる。つまり、`DT-PARENT`は、実質`X(10)`であって、中身が`DT-CHILD1`と`DT-CHILD2`を合わせたものになる。もちろん`DISPLAY`で`DT-CHILD1`とすれば、いつもどおり`DT-CHILD1`のデータだけ出てくる。

逆に`ACCEPT`とかで`DT-PARENT`に`HELLOWORLD`と入れると、`DT-CHILD1`に`HELLO`、`DT-CHILD2`に`WORLD`と、分けられて入る。

![FIG1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163288/5ef80d70-29f6-5f09-e0dd-676a20e4fbad.png)


また、上の例から、更に05、07と続けさせることもできる。

注意点として、グループの親に`PICTURE`句をつけてはならないこと。グループのレベル番号が一番高い要素にだけつける。

## 無名項目：`FILLER`句

`FILLER`句は固定の値や文字を持ちたいときに使う。なので定義時に初期値を入れると、後から変更ができなくなる。また、`FILLER`句は予約語なのだが、データ項目名として使う。で、定義方法と初期値の入れ方なのだが、

```
レベル番号  FILLER  PIC 型(文字数) VALUE '初期値'.
```

とする。

ここで気づいた方がいると思うが、「データ項目名に予約語の`FILLER`を入れるということは、複数個`FILLER`作ったら、同じデータ項目名がいくつもできて、`DISPLAY`とかするときに指定できないじゃん」ってなるが、そもそも`FILLER`句を使って定義したものは他の構文で直接使えない。

じゃあどうするんだってなるが、`FILLER`は前項のレベル番号の子の一部として使う。そうすることで、データのフォーマットを自分で作ることができるようになる。例えば、帳票出力するとき、定形文字があったりすると、使えたりする。

```:サンプル 
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  PRINT-NAME.
         03  FILLER           PIC X(06) VALUE 'NAME: '.
         03  IN-NAME          PIC X(10).
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
           ACCEPT             IN-NAME FROM CONSOLE.
           DISPLAY            PRINT-NAME.
           STOP RUN.
```

```:実行結果
TAROH <-自分で入れたもの
NAME: TAROH
```

こうしておけば、間違って書き換えられることもないし安心。

## 数値型の種類

今まで、`PICTURE`句の後ろに`X`とか`9`とか使ってきた。文字型は`X`でこれといってそれ以上の特徴はないけど、数値型の定義方法が色々あってちょっと複雑。ここではその中の一例を挙げる。

### ゾーン10進数の整数桁数指定：`9`

一番良く使う整数の型。入れることのできる桁数を任意で決める。もし数値5桁のデータ項目に9999を代入すると、09999というように0埋めされる。また、計算したデータを入れるとき、小数点以下は切り捨て。

### ゼロサプレス（Z編集）：`Z`

ゼロサプレスについて書いておくと

> ゼロサプレスとは、数値を文字として表現・表示する際に、先頭あるいは末尾の余分な「0」を削除したり空白で置き換えたりする処理のこと。
> 
> _[ゼロサプレスとは - IT用語辞典](http://e-words.jp/w/%E3%82%BC%E3%83%AD%E3%82%B5%E3%83%97%E3%83%AC%E3%82%B9.html)_

つまり、5桁データ項目に9999と入れても9999と0埋めされない。

が、ここには罠がある。例えばこのプログラム。

```:サンプル
      *---1---------2---------3---------4---------5-----
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  Z-EDIT             PIC Z(02).
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
           MOVE               0 TO Z-EDIT.
           DISPLAY            Z-EDIT.
           STOP RUN.
```
出力は0だと思うかもしれないが、実は何も出ない。`Z`は1の位が0のときもお構いなしに消してくるので、対策としては、`Z(02)`を`Z9`としてやる必要がある。

### 小数点以下の桁数指定: `V`

そう、整数を入れるデータの桁数の指定と、小数点以下の桁数の指定は違う。例えば、999.99という数字を入れたいときは、

```:構文
01  DT-NAME  PIC 9(03)V9(02).
```

としてやらねばならない。

### 符号付きの値: `S`

`S`をつけると、符号付きの値として扱われる。例えば、符号付き整数なら`S9`とする。今までのように`S`がついていないものは、正の値として扱われる。

### パック10進数: `COMP-3`

まぁ上でゾーン10進数と書いたから勘付いていたとは思うが…。

一応簡単に説明しておくと、8ビット（1バイト）を使って、どう数値を表現するかの違い。ゾーン10進数は1バイトで1桁（前4バイトをゾーン部、後ろ4バイトにその桁の数字）、パック10進数は4ビットで1桁なので、1バイトで2桁の数値を表現できるということになる。

![FIG2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163288/7ade2e53-218e-7735-cd72-727773ad4f31.png)

さて話を戻し、パック10進数の定義方法を見ていく。

```:構文
01  DT-NAME  PIC S9(03) COMP-3.
```

いつもの構文にプラス`COMP-3`を付ける必要がある。

## 形象定数

要はCOBOL側で予め決められている文字リテラル。研修教材には以下のものがあった。

|形象定数|中身|有効型|
|:---:|:---|:---|
|`SPACE`|文字のスペース。必要数は自動で判断|`X`のみ|
|`ZERO`|文字または数値の0|すべて|
|`HIGH-VALUE`|コンピュータが表現できる文字の最大値|`X`のみ|
|`LOW-VALUE`|コンピュータが表現できる文字の最小値|`X`のみ|

調べながらやってると他にも出てきたりしたが、とりあえず今回はこれだけ。

### `HIGH-VALUE`・`LOW-VALUE`って何が入っているんだろう

研修で教えてくれなくて、興味があって[ググってみた](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1195531606)。

`HIGH-VALUE`は、16進数`FF`がセットされる。印字や画面出力はできないが、比較には使えて、`X`のときは最大を保証する。

逆に`LOW-VALUE`は、全体に16進数の0埋めされる。

## 今回はここまで

本当は`S9`から`X`のデータ移行で直接`MOVE`できないぞってことも書きたかったのだけれど、なんかできてしまってよく分からないので、今回の投稿は保留。これは勉強し直してから。

今回は画像も付けたので、すごい長かった。でも研修で教わらないことも知ることができたので楽しかった。

[続き](https://qiita.com/zama_8722/items/e59f2f62aa921407b442)（2019/07/14作成）