【実務・中級編】PIC ‘9’とPIC ‘Z’による数値編集の差異 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは。基幹システムの現場で日々、COBOLの山と格闘しつつも、心の中ではPL/Iの奥深さに魅入られている古参のシステムアーキテクトだ。

今日も保守チームの後輩から「先輩、オンライン画面の金額表示で、なぜか頭に不必要なゼロがいっぱいついてダサいんです。あと、帳票出力だと逆にゼロが消えちゃって計算に使えません。どう直せばいいですか?」と質問を受けた。
まったく、これだから新人は……と言いたいところだが、待てよ。PL/Iのピクチャ編集(PIC)、特に `9` と `Z` の挙動の裏にある「予約語を持たない自由度の高さ」と「コンパイラが裏でやってくれるデータ変換の罠」は、中堅エンジニアでも時々ハマるポイントだ。

今回は、VSAMファイルやレコード入出力、そして電卓を叩くようにシビアな金額を扱う現場で絶対に知っておくべき、`PIC ‘9’` と `PIC ‘Z’` の数値編集の差異について、実務に即したコードを交えながら徹底的に叩き込んでやろう。

1. PL/Iのピクチャ編集:なぜ `9` と `Z` で挙動が違うのか?

PL/Iの最大の特徴の一つは、COBOLのような「ガチガチの予約語の束縛」が非常に少ない点にある(識別子の命名規則さえクリアしていれば、キーワードですら文脈によって変数名として使えてしまうほどの自由度だ)。しかし、データ定義(DECLARE)におけるピクチャ仕様(PICTURE)に関しては、データを見やすく整形するための方言ならぬ、厳格な表現ルールが存在する。

数値編集文字において、最も頻繁に登場するのが以下の2つだ。

  • `PIC ‘9’`(数字位置指定文字)
  • 有効な数字をそのまま表示する。対応する値が存在しない(高位の桁)場合は、「先行ゼロ(Leading Zero)」として `0` を埋める
  • 計算結果の保持や、固定長のレコードレイアウトで桁位置を厳密に揃えたい場合に必須。
  • `PIC ‘Z’`(ゼロ抑制文字)
  • 対応する値がゼロである場合、それをスペース(空白)に置き換えて抑制(Suppress)する。ただし、有効な数字に挟まれたゼロや、有効数字より下位のゼロは抑制されない。
  • 帳票や画面上の金額表示で、`00012,345円` を ` 12,345円` のようにスッキリ見せるために使う。

実務において、この差異を理解していないと何が起きるか?
VSAMファイルから読み込んだ生データをそのまま `PIC ‘Z’` を使った画面出力項目に直接MOVEしてしまい、内部の数値属性との暗黙の型変換(あるいはデータ例外)で夜間バッチが S0C7(データ例外アボート) を引き起こす、なんていう笑えない事故が起発発するのだ。

2. 実務で直面するトラブルとONユニットによる防御

メインフレームのバッチ処理では、外部ファイル(QSAMやVSAM)から読み込んだレコードを構造体(DECLAREのMAJOR/MINOR構造)にマッピングする。
ここで、データベース上の純粋な計算用フィールド(例: `PIC ‘S9(9)V99COMP’`)から、帳票・画面用の文字編集フィールド(`PIC ‘ZZZ,ZZZ,ZZZ.99’`など)へ値を転記する際、データ定義のミスマッチがあると、コンパイルは通っても実行時エラーの温床になる。

さらに、PL/Iには強力な例外処理機構である ONユニット(ON-Unit) がある。数値編集のオーバーフローや文字化けが発生した際、これを捕捉せずに放置するとジョブが異常終了してしまうため、現場の標準コーディングでは必ず `CONVERSION` 条件などを捕捉する設計が求められる。

それでは、実際のバッチプログラムを想定したコードを見ていこう。

3. 実践PL/Iプログラム:`9` と `Z` の使い分けと編集処理

以下のサンプルは、VSAMから読み込んだ数値データを、帳票出力用(先行ゼロ抑制:`Z`)と、電文・固定長ログ出力用(ゼロ埋め:`9`)にそれぞれ編集し、出力する典型的なバッチ処理の抜粋だ。

1
/ ========================================================== /
/ プログラム名: MTRX01C /
/ 処理概要 : 顧客売上データのピクチャ編集(9とZの差異) /
/ ========================================================== /
MTRX01C: PROC OPTIONS(MAIN);

/ — 1. 外部ファイル(VSAM/QSA)のレコード定義 — /
DCL 1 IN_RECORD,
5 IN_CUST_ID CHAR(5), / 顧客ID /
5 IN_SALES_AMT DEC FIXED(9,2);/ 売上金額(計算用) /

/ — 2. 編集後出力用の変数定義 — /
DCL 1 OUT_PRINT_REC,
/ 帳票用:先行ゼロを抑制し、視認性を高める(Z使用) /
5 PRT_CUST_ID CHAR(5),
5 FILLER CHAR(2) VALUE ‘ ‘,
5 PRT_SALES_Z PIC ‘ZZZ,ZZZ,ZZ9.99’;

DCL 1 OUT_LOG_REC,
/ ログ・固定長電文用:桁位置を固定するためにゼロ埋め(9使用) /
5 LOG_CUST_ID CHAR(5),
5 FILLER CHAR(2) VALUE ‘ ‘,
5 LOG_SALES_9 PIC ‘000000009.99’;

/ — 3. 制御フラグおよびワーク変数 — /
DCL EOF_FLAG CHAR(1) INIT(‘OFF’);
DCL RAW_AMOUNT DEC FIXED(11,2);

/ — 4. 異常系ハンドリング(ONユニット) — /
/ データ変換エラー(CONVERSION)を捕捉し、安全に処理を継続する /
ON CONVERSION
BEGIN;
PUT SKIP LIST(‘【警告】データ変換エラーが発生しました。値をゼロとして扱います。’);
RAW_AMOUNT = 0;
END;

/ — 5. メイン処理ループ — /
PUT SKIP LIST(‘=== 売上データ編集処理開始 ===’);

/ ダミーの入力シミュレーション(実際はREAD FILE(…)を使用) /
/ ここではあえて少額データを扱い、Zと9の挙動の違いを浮き彫りにする /
IN_CUST_ID = ‘C0012’;
IN_SALES_AMT = 1250.50;

/ — 6. 編集と代入の実行 — /
/ 計算用フィールドから編集用フィールドへの暗黙の型変換(MOVE) /
PRT_CUST_ID = IN_CUST_ID;
PRT_SALES_Z = IN_SALES_AMT; / PIC ‘Z’ による編集 /

LOG_CUST_ID = IN_CUST_ID;
LOG_SALES_9 = IN_SALES_AMT; / PIC ‘9’ による編集 /

/ — 7. 結果出力(BUILTIN関数 TRIMを活用) — /
PUT SKIP EDIT (‘[帳票出力 (PIC Z)] 顧客:’, PRT_CUST_ID,
‘ 金額:’, TRIM(PRT_SALES_Z))
(A, A, A, A);

PUT SKIP EDIT (‘[電文出力 (PIC 9)] 顧客:’, LOG_CUST_ID,
‘ 金額:’, LOG_SALES_9)
(A, A, A, A);

/ もう一つのケース:金額が「0円」の場合の挙動確認 /
IN_SALES_AMT = 0;
PRT_SALES_Z = IN_SALES_AMT;
LOG_SALES_9 = IN_SALES_AMT;

PUT SKIP LIST(‘— ゼロ値入力時の挙動テスト —‘);
PUT SKIP EDIT (‘[帳票出力 (PIC Z)] 0円の場合: [‘, TRIM(PRT_SALES_Z), ‘]’) (A, A, A);
PUT SKIP EDIT (‘[電文出力 (PIC 9)] 0円の場合: [‘, TRIM(LOG_SALES_9), ‘]’) (A, A, A);

PUT SKIP LIST(‘=== 売上データ編集処理終了 ===’);

END MTRX01C;

4. コードの解説とシニアからの実務アドバイス

上記のコードを実行、あるいは脳内コンパイルしたときに、出力結果がどうなるかイメージできるだろうか。

`PIC ‘ZZZ,ZZZ,ZZ9.99’` の挙動

  • `1250.50` を代入した場合、高位の `Z` で指定された桁のゼロはすべてスペースに置換される。
  • 結果として、` 1,250.50` のように出力される。
  • もし値が `0` の場合、最後の `9` が指定されているため、最低1桁の `0` が残り、さらに `Z` の影響で ` 0.00` (あるいはコンパイラの仕様に応じたフォーマット)になる。ここで全部スペースにしたい場合は、整数部の全桁を `Z` にし、小数点以下の端数処理に注意する必要がある。

`PIC ‘000000009.99’` または `PIC ‘9(9)V99’` の挙動

  • こちらは先行ゼロを維持する。`1250.50` であれば `000001250.50` となり、固定長レコードのレイアウト(オフセットとレングス)が厳密に決まっている旧来のホスト間連携や、後続のプログラムへ渡すインターフェースファイルにおいて、桁ズレを防ぐための絶対的な砦となる。

実務上の注意点:

1. 演算への再利用の禁止
一度 `PIC ‘Z’` や `PIC ‘9’` などの文字ピクチャ(Character Picture)に変換したデータは、内部的にはキャラクタ型(文字)として扱われる。これをそのまま算術演算の右辺に置くと、コンパイラが暗黙の変換を行うか、最悪の場合は前述の `CONVERSION` エラーを引き起こす。計算はあくまで `DECIMAL FIXED` や `BINARY FIXED` の数値型のまま行い、画面やファイルに出力する直前の最終ステップでピクチャ編集を行うのが鉄則だ。
2. オーバーフロー(星印 “ の発生)
編集先のピクチャサイズが、入力される数値の桁数に対して小さすぎると、PL/Iコンパイラ(および実行時ルーチン)はデータを切り捨てるのではなく、アスタリスク(“)でフィールドを埋め尽くす。帳票に金額の代わりに `` が並んでいる苦情チケットを深夜に起こされたくなければ、定義桁数は元データに対して十分な余裕を持たせることがシニアからの切なる願いだ。

レガシーシステムの寿命は、私たちが思うよりもずっと長い。そして、そこで動くPL/Iのコードの一行一行には、先人たちが苦労して培った業務ロジックが詰まっている。
「なぜここで `Z` なのか」「なぜここは `9` なのか」その理由を正確に理解し、意図を持ったコーディングを心がけてほしい。君たちの手による堅牢なコード改修が、明日も日本の基幹インフラを静かに支え続けるのだから。

タイトルとURLをコピーしました