【実務・中級編】STREAMファイルにおけるLINESIZEとPAGESIZEの制御と改行コードの扱い – PL/Iの基本構文とデータ制御実践ガイド

こんにちは、後輩くん。
今回の改修、なんだか神妙な顔をして取り組んでいると思ったら、例の「SYSPRINTの出力が意図せぬところで改行される」「テスト環境と本番環境で帳票のレイアウトが崩れる」という、メインフレーム開発では誰もが一度はハマる沼に足を取られていたんだね。

いいかい、よく聞いてほしい。PL/Iという言語は、他の言語(COBOLやCなど)とは一味も二味も違う独自の哲学で動いている。特にSTREAM(ストリーム)入出力における `LINESIZE` と `PAGESIZE` の挙動、そして物理レコード(ブロック)との関係を理解していないと、深夜のバッチ障害で冷や汗をかくことになる。

今日は、その仕組みの裏側と、実務で絶対に押さえておくべきコーディングの極意を徹底的に叩き込んであげるから、しっかりついてきなさい。

—

1. 予約語を持たないPL/Iの懐の深さと「罠」

まず、PL/Iの基本構文の前提として、君も知っている通り、この言語には厳密な意味での「予約語(Reserved Words)」が存在しない。
`IF` や `LINE`、さらには `LINESIZE` さえも、コンテキストによってはただの変数名として使うことができてしまう。

/ こんな狂気染みた変数宣言も、PL/Iの文法上はエラーにならない /
DCL LINESIZE FIXED DEC(5) INIT(132);

コンパイラは、前後の文脈(コンテキスト)から「これはオプションキーワードか、それともプログラマが定義した識別子か」を完璧に判別する。この柔軟性は強力なんだけど、裏を返せば、うっかりシステム組み込みのキーワードを上書きしてしまったり、オプションのスペルミスをコンパイラが「新しい変数への代入や参照だ」と勘違いして、意図しないシンタックスエラーやロジックバグを引き起こす温床になる。

だからこそ、実務のコーディング標準では、識別子の命名規則を厳格に管理する必要があるんだ。

—

2. STREAM出力の心臓部:LINESIZE と PAGESIZE の正体

さて、本題のSTREAMファイル(主に `SYSPRINT` などの帳票出力やログ出力)における制御の話だ。

レガシーなメインフレームのバッチジョブでは、JCLの `SYSUT1` や `SYSPRINT` に対して、DCBパラメータ(RECFM=FBAやVBAなど)を指定して出力を行う。しかし、PL/Iのランタイム(LE: Language Environment)は、プログラマが指定した `LINESIZE` と `PAGESIZE` に従って、論理的な行とページの管理を勝手に行う。

ここで重要なポイントを整理しておこう。

  • `LINESIZE(n)`: 1論理行あたりの最大文字数を指定する。この長さを超えてデータを書き込もうとすると、コンパイラ(ランタイム)が自動的に強制改行(キャリッジコントロールの挿入)を行う。
  • `PAGESIZE(m)`: 1ページあたりの最大行数を指定する。この行数を超えると、自動的に改ページ(ページオーバーフロー)が発生し、必要であればヘッダーの再出力制御(ON ENDPAGEユニット)が呼び出される。

改行コードと物理レコードの自動付与ロジック

C言語やJavaのような「`\n`(改行文字)」をプログラマが明示的に埋め込む世界とは違う。PL/IのSTREAM出力では、`PUT LIST` や `PUT EDIT` でデータを流し込むと、ランタイムが `LINESIZE` で指定されたバッファサイズを監視し、そこに達するか、あるいは明示的な `SKIP` や `PAGE` が実行されたタイミングで、適切な改行制御文字(ASA制御文字や、レコード長プレフィックス)を付与して物理レコードを書き出す。

もし、JCL側のLRECL(論理レコード長)と、PL/Iコード側の `LINESIZE` の整合性が取れていないとどうなるか?
JCL側で切り詰められたり、意図しないところでレコードが分断され、出力帳票のフォーマットがグチャグチャになるというわけだ。

—

3. 実践!堅牢なSTREAM出力プログラムのサンプルコード

百聞は一見に如かず。実際に `LINESIZE` と `PAGESIZE` を制御し、`ON ENDPAGE` ユニットを用いてページ制御を行う堅牢なPL/Iプログラムのサンプルを見せよう。

大文字ベースの記述、適切なインデント、そして `BUILTIN` 関数の活用に注目してほしい。

STRPUTC: PROC OPTIONS(MAIN);

/ ファイルの定義: STREAM出力、PRINTER属性を付与 /
DCL OUTPRT FILE STREAM OUTPUT PRINT;

/ 制御用変数の定義 /
DCL W_LINE_COUNT FIXED BIN(31) INIT(0);
DCL W_PAGE_COUNT FIXED BIN(31) INIT(0);
DCL I FIXED BIN(31);
DCL
MSG_LINE CHAR(100) VARYING,
FMT_DATE CHAR(10) BUILTIN; / 日付取得用ビルトイン関数想定 /

/ 異常系およびページ制御のためのONユニット定義 /
ON ENDPAGE(OUTPRT) BEGIN;
W_PAGE_COUNT = W_PAGE_COUNT + 1;
PUT FILE(OUTPRT) PAGE; / 物理的な改ページを発行 /
PUT FILE(OUTPRT) EDIT (‘==================================================’) (A);
PUT FILE(OUTPRT) SKIP(1);
PUT FILE(OUTPRT) EDIT (‘月次売上レポート P. ‘, W_PAGE_COUNT) (A, F(4));
PUT FILE(OUTPRT) EDIT (‘==================================================’) (A);
PUT FILE(OUTPRT) SKIP(2);
END;

/ ファイルオープン: LINESIZEとPAGESIZEを明示的に指定 /
/ ※JCL側のDCB(RECFM=FBA, LRECL=133)と整合させること /
OPEN FILE(OUTPRT)
STREAM
OUTPUT
LINESIZE(132)
PAGESIZE(60);

/ 初回ヘッダー出力のための強制PAGE発行 /
SIGNAL ENDPAGE(OUTPRT);

/ メイン処理ループ:ダミーデータを150行出力してページ制御をテストする /
DO I = 1 TO 150;

/ 文字列の組み立て(TRIMなどのBUILTIN関数を活用) /
MSG_LINE = ‘データレコード連番: ‘ || TRIM(CHAR(I)) || ‘ – 正常処理完了’;

/ PUT EDITによる精密なレイアウト出力 /
/ LINESIZE(132)を超えないよう、フォーマットを厳密に管理する /
PUT FILE(OUTPRT) EDIT (MSG_LINE) (A(100));

/ 次の行へスキップ(内部でLINESIZEの残余と改行をハンドリング) /
PUT FILE(OUTPRT) SKIP(1);

END;

/ ファイルクローズ /
CLOSE FILE(OUTPRT);

PUT SKIP LIST(‘全処理が正常終了しました。’);

END STRPUTC;

—

4. 現場のベテランから送る「デバッグと設計の金言」

このコードと仕様に関して、現場で絶対に覚えておいてほしい実務の知恵をいくつか授けよう。

1. JCLのLRECLとLINESIZEのミスマッチに殺されるな
`LINESIZE(132)` と指定した場合、ASA制御文字(1バイト)が先頭に付く環境では、JCL側の `LRECL` は少なくとも `133` 以上(RECFM=FBAなど)でなければならない。ここをケチったり合わせ忘れたりすると、OS側でレコードが強制切捨て(TRUNCATION)され、データが破損する。移行プロジェクトで最も多いポカミスの一つだ。
2. 無制限(STREAM)に甘えるな
デバッグが容易だからといって、複雑なレコードレイアウトのファイルを `STREAM` で処理しようとするのはご法度だ。固定長・可変長のバイナリや構造化データを扱うなら、原則として `RECORD` 入出力を選択すべきであり、`STREAM` はあくまで「人間が読むための帳票(SYSPRINT等)」や「簡易ログ」に限定しなさい。
3. ON ENDPAGEのスコープを意識せよ
`ON ENDPAGE` ユニットは、ファイルオープン状態のスコープ内で正しく機能するように配置すること。サブルーチンを跨ぐようなグローバルな割り込み設計にすると、どのファイルに対する改ページなのかが曖昧になり、保守性が最悪になる。

—

どうだい、少しは `LINESIZE` と改行制御の裏側が見えてきたかい?
メインフレームの足回りは古臭く見えるかもしれないが、言語仕様の根底にある設計思想は非常にロジカルで美しい。一つひとつのオプションが「なぜそこに存在するのか」を突き詰めていけば、君も一人前のPL/Iアーキテクトになれるはずだ。

さて、コードの修正に戻るとしようか。何か詰まったら、いつでも私のところに来なさい。

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