【実務・中級編】PICTURE属性の内部表現と編集文字の役割 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近入った若手が「PL/Iって変数名の予約語がないから何でも使えて気楽ですね」なんて笑いながらコードを書いてるのを見て、思わず背筋が凍ったんだよ。おいおい待て、予約語がないってことは、うっかり変数名に `IF` や `TOTAL` を書いてもコンパイルエラーにならない代わりに、コンパイラが文脈から自力で解釈しなきゃいけない地獄の扉が開くってことだぞ。勘違いして変なバグを踏む前に、今日のテーマである「PICTURE属性の内部表現と編集文字の役割」について、骨の髄まで叩き込んでやる。

現場でVSAMのマスターファイルを叩いたり、夜間バッチで膨大な帳票用レコードを組み立てたりするとき、このPICTURE(PIC)句の挙動を完璧に理解していないと、本番環境で「ゾーン10進数がデータ例外(S0C7)を起こした!」って真夜中に叩き起こされる羽目になる。さあ、コーヒーでも飲みながら、我が社の基幹システムを支えるPL/Iの深淵をのぞいてみようか。

—

1. PICTURE属性の真実:メモリ上の内部表現とゾーン・パックの闇

PL/Iの `PICTURE` 属性は、単なる「見た目のフォーマット指定」だと思ったら大間違いだ。あれは「メモリ上でデータがどのように物理的に表現されているか」を規定する極めてハードウェア寄りのデータ定義なのだ。

IBMメインフレーム(z/Architecture)の世界では、数値データは主に以下の2つの形式でメモリ上に存在している。

1. ゾーン10進数(DISPLAY形式):1桁につき1バイトを消費する。人間がダンプリストを読んだときにそのまま数字が見える。符号(正負)は最上位バイトのゾーン部分(上位4ビット)に押し込まれる。
2. パック10進数(COMP-3形式):1バイトに2桁の数字を詰め込み、最後の半バイト(4ビット)に符号(C, D, Fなど)を格納する。ストレージ効率が良く、計算も速い。

PIC ‘9’, ‘V’, ‘S’ の正体

さて、コードでよく見る `PIC ‘S9(07)V99 COMP-3’` なんて記述。これを分解して、ベテランの視点でその内部構造を暴いてやろう。

  • `S` (Sign): 符号を持つことを示す。メモリ上ではパック形式の場合、最後のバイトの右側ニブル(下位4ビット)に正なら `C` または `F`、負なら `D` が保持される。
  • `9` (Numeric): 1桁の数字を表す。
  • `V` (Implied Decimal Point): 仮想小数点だ。ここがポイントなんだが、`V` 自体はメモリ上の領域を1バイトも消費しない。単に「ここを境に小数点の位取りがこうなっている」というコンパイラへの申し送り事項に過ぎない。

例えば、`DCL WS-AMT PIC ‘S9(5)V99’ COMP-3;` と宣言した場合、合計7桁の数字と符号を格納するため、メモリ上では 4バイト(32ビット) の領域が確実に確保される。もし `V` を忘れて実小数点 `.` を書いてしまうと、それは編集文字扱いになり、計算に使えなくなるか、無駄な内部変換が発生してバッチのパフォーマンスがガタ落ちする。ここ、テストに出るくらい重要だぞ。

—

2. 編集文字の役割:表示・出力用PICの罠

次に、画面や帳票、あるいは後続システムへ渡すためのファイルに出力する際の「編集文字(`Z`, `.` `,` `$` など)」についてだ。

これらはデータをメモリ上で「加工」して見栄えを良くするものだが、編集属性が付いた変数は、原則として算術演算の左辺(計算結果の代入先)に使うべきではない。なぜなら、メモリ上の表現が文字(DISPLAY)に変わり、カンマや円マークがパディングされてしまうため、純粋な数値としての演算ができなくなるからだ。

代表的な編集文字の挙動

  • `Z` (Zero suppression): 先行するゼロをブランクに置き換える。例えば `PIC ‘ZZZ,ZZ9.99’` の場合、`00123.45` は ` 123.45` と綺麗に右寄せで出力される。
  • `V` との決別: 編集文字を使うときは、仮想小数点 `V` ではなく、実小数点 `.` を置くことが多い。これにより、データは文字として確定する。

—

3. 実践コード:VSAM入出力とONユニット、そしてBUILTIN関数の活用

百聞は一見に如かず。実際のメインフレームのバッチジョブを想定したPL/Iのサンプルコードを用意した。
ここでは、VSAM(KSDS)から生データを読み込み、内部で `COMP-3` の数値をバリデーションしつつ、例外が発生した際には `ON` ユニットで優雅にトラップし、編集済みフィールドでレポート出力する一連の流れを示そう。

1
MSTR_UPDATE: PROC OPTIONS(MAIN);

/ —————————————————————- /
/ ファイル・VSAM定義 /
/ —————————————————————- /
DCL MSTR_FILE FILE RECORD INPUT
ENVIRONMENT(CONSECUTIVE); / 実務ではVSAM KSDS等に置き換え /

DCL RPT_FILE FILE RECORD OUTPUT
ENVIRONMENT(PRINTER);

/ —————————————————————- /
/ レコード構造体(入力レイアウト:ゾーンとパックの混在) /
/ —————————————————————- /
DCL 1 IN_REC,
5 IN_EMP_ID CHAR(5), / 社員ID(文字) /
5 IN_BASE_SAL PIC ‘S9(7)V99’ COMP-3, / 基本給(パック10進数) /
5 IN_ALLOWANCE PIC ‘S9(5)V99’ COMP-3; / 手当(パック10進数) /

/ —————————————————————- /
/ 作業変数・編集用定義 /
/ —————————————————————- /
DCL WS_TOTAL_SAL PIC ‘S9(9)V99’ COMP-3; / 計算用内部変数 /
DCL WS_EDIT_SAL PIC ‘ZZZ,ZZZ,99.99’; / 編集済み出力用 /
DCL EOF_FLAG BIT(1) INIT(‘0’B); / 終了フラグ /

/ —————————————————————- /
/ ONユニット:データ例外(S0C7相当)のトラップ /
/ —————————————————————- /
ON CONVERSION
BEGIN;
PUT SKIP EDIT (‘【エラー】数値データ例外発生。レコードをスキップします。ID: ‘, IN_EMP_ID)
(A, A);
/ 異常データを検知した際の安全なリトライまたは処理継続の制御 /
GOTO READ_NEXT;
END;

/ ファイルオープン /
OPEN FILE(MSTR_FILE) INPUT,
FILE(RPT_FILE) OUTPUT;

/ 初回読み込み /
READ FILE(MSTR_FILE) INTO(IN_REC);

DO WHILE(^EOF_FLAG);

/ ———————————————————— /
/ 算術演算とBUILTIN関数の活用 /
/ ———————————————————— /
/ ROUNDビルトイン関数を使い、小数点以下第3位を四捨五入して第2位まで保持 /
WS_TOTAL_SAL = ROUND(IN_BASE_SAL + IN_ALLOWANCE, 2);

/ 編集項目への代入(ここで内部数値から表示用文字へ変換される) /
WS_EDIT_SAL = WS_TOTAL_SAL;

/ 帳票への出力 /
WRITE FILE(RPT_FILE) FROM(
‘社員ID: ‘ || IN_EMP_ID || ‘ 支給総額: ‘ || WS_EDIT_SAL
);

READ_NEXT:
/ 次レコード読み込み(ATEND条件の処理) /
READ FILE(MSTR_FILE) INTO(IN_REC);

END;

/ ファイルクローズ /
CLOSE FILE(MSTR_FILE),
FILE(RPT_FILE);

RETURN;

END MSTR_UPDATE;

—

4. ベテランからの現場の教訓:デバッグのコツ

このコードを見て、「ほう、`ON CONVERSION` で `S0C7` をトラップできるのか、便利だな」と思ったそこの君。甘い。例外をトラップして `GOTO` で逃げるのは応急処置にすぎない。根本原因は、大抵の場合、「外部から流れてきたインプットファイルにゴミデータ(スペースや不正な文字コード)が混入していること」にある。

メインフレームのマイグレーションやオープン系連携において、最も多い障害がこの「PIC属性の不一致によるデータ例外」だ。
ソースコードを改修する際は、必ず以下の3点を確認する癖をつけなさい。

1. COBOLのCOPY句や他言語のレイアウト定義と、PL/Iの `PIC` 句(特に桁数と `COMP-3` の有無)が1バイトたりともズレていないか。
2. 計算を行うフィールドに、意図せず編集文字(`,` や `$`)が入った変数を直接割り当てていないか。
3. `V`(仮想小数点)の位置を勘違いして、スケールファクター(桁合わせ)の計算ミスをしていないか。

PL/Iは言語仕様が懐深く、書き手の自由度が高い分だけ、設計者の技量がそのままコードの品質に跳ね返る。変数名に予約語がないという緩さに油断せず、メモリ上のデータ構造を頭の中でビジュアライズしながらコードを書ける、真のプロフェッショナルを目指してくれ。期待しているぞ。

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