【入門編】STRINGビルトイン関数による構造体の一括操作 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語の経験をお持ちの方にとって、IBMメインフレームの世界、そしてそこで長年君臨してきた「PL/I(ピーエルワン)」という言語は、どこか要塞のような、近寄りがたい雰囲気を醸し出しているかもしれません。

「なんだか変数名のルールが緩い気がする……」
「構造体をそのまま文字列として扱いたいのに、COBOLの `REDEFINES` みたいな書き方が通じないぞ?」

そんな戸惑いを抱えていませんか?大丈夫です。怖がる必要は全くありません。今回は、PL/Iのちょっとユニークでパワフルな機能、`STRING`ビルトイン関数による構造体の一括操作について、メモリの裏側まで優しく紐解いていきましょう。

—

1. まずは安心してください:PL/Iには「厳密な予約語」がほとんどありません

JavaやCOBOLを触ってきた方なら、「この単語はシステム予約語だから変数名に使えない!」という壁に何度もぶつかったことでしょう。

しかし、PL/Iの大きな特徴(そして初学者が最初に驚くポイント)は、「言語組み込みのキーワード(KEYWORD)であっても、文脈によっては変数名として自由に使えてしまう」という懐の深さです。
例えば、`IF` や `READ` といった命令すら、プログラマが変数名として宣言してしまえば、コンパイラは前後の文脈から「あ、今回は変数ね」「ここは命令ね」と空気を読んで賢く解釈してくれます。

「えっ、そんなのバグの元じゃん!」と思われるかもしれませんが、レガシーシステムの改修ではこの柔軟さに救われることも多いのです。とはいえ、後からコードを読む人が混乱しないよう、実務では予約語っぽい名前をあえて変数に使うのは避けるのが大人のマナーですね。

—

2. 構造体って、メモリ上ではどう並んでいるの?

さて、本題の「構造体の一括操作」に入る前に、データがメモリ上でどう配置されているか(メモリレイアウト)のイメージをつかみましょう。

PL/Iでは、階層構造を持つデータを `DECLARE`(略して `DCL`)で以下のように定義します。

1
DCL 1 顧客情報,
2 顧客ID CHAR(5),
2 顧客名 CHAR(20),
2 購買フラグ CHAR(1);

JavaのオブジェクトやCOBOLの01レベルグループ項目に似ていますよね。
メインフレームのメモリ上では、この構造体は隙間なく、左から右へピタリと連続して配置されます。

  • `顧客ID`(5バイト)
  • `顧客名`(20バイト)
  • `購買フラグ`(1バイト)

合計すると、この `顧客情報` という構造体は、綺麗に26バイトの連続した領域を占めていることになります。

—

3. `STRING` ビルトイン関数とは?(魔法の一括操作)

「この26バイトの構造体データを、ファイルに出力する前やログに吐き出す前に、一撃で一つの『26バイトの文字列』として扱いたい!」
そんなときに登場するのが、今回主役の `STRING` 関数 です。

通常、構造体の中身を操作するには `顧客情報.顧客名 = ‘山田太郎’;` のように一つずつアクセスする必要がありますが、`STRING(顧客情報)` と記述すると、コンパイラに対して「この構造体全体を、中身の境界を無視して、ただの26バイトの文字の塊(CHAR(26))として見なせ!」と指示を出すことができます。

百聞は一見にしかず。実際のコードを見てみましょう。

実用的なPL/Iコード例

1
/ ======================================================== /
/ 構造体のSTRING関数による一括操作サンプル /
/ ======================================================== /
TEST_PGM: PROC OPTIONS(MAIN);

/ 1. 構造体の定義 /
DCL 1 伝票データ,
3 伝票番号 CHAR(6), / 伝票番号 (例: ‘A00123’) /
3 処理日付 CHAR(8), / 処理日付 (例: ‘20231025’) /
3 金額 FIXED DEC(7,2); / 漢字や数字の混在に注意! /

/ 2. 一括操作用の文字変数(構造体の合計サイズに合わせる) /
DCL ワークエリア CHAR(22);

/ 3. 構造体に値をセット /
伝票番号 = ‘B99988’;
処理日付 = ‘20231101’;
金額 = 12345.67; / ※注意:FIXED DECIMALは内部でパック十進数等になります /

/ —————————————————- /
/ STRING関数を使った一括代入(構造体 -> 文字列) /
/ —————————————————- /
WORKAREA = STRING(伝票データ);

PUT SKIP LIST(‘ワークエリアの中身: ‘, WORKAREA);

/ —————————————————- /
/ 逆に、文字列から構造体へ一括流し込み(文字列 -> 構造体)/
/ —————————————————- /
STRING(伝票データ) = ‘X11111202312319876543’;

PUT SKIP LIST(‘復元された伝票番号: ‘, 伝票番号);
PUT SKIP LIST(‘復元された処理日付: ‘, 処理日付);

END TEST_PGM;

—

4. 気をつけて!型変換とメモリの「罠」

この `STRING` 関数、ファイルI/Oのバッファリングや電文のアンパック(電文解析)において非常に強力な武器になるのですが、一つだけ絶対に知っておかなければならない恐ろしい罠があります。

それは、「構造体の中に、文字(CHAR)以外のデータ型(数値型など)が混ざっているとき」の挙動です。

上記のコード例で、`金額` に `FIXED DEC(7,2)` という数値型を使いました。
Javaの `int` や `double`、COBOLの `COMP-3`(パック十進数)を想像してください。これらは「文字」ではなく「数値データ(バイナリやゾーン・パック形式)」としてメモリに格納されます。

ここに `STRING` 関数を適用すると、コンパイラは「問答無用でそのバイナリ領域を文字として切り貼り」します。

  • 文字列として画面に出力したりファイルに書いたりしたとき、数値部分が文字化けして「謎の制御文字」のようになる。
  • 外部から受け取った文字列をそのまま `STRING(構造体) = 外部変数;` で流し込んだ際、数値項目の定義とズレてしまい、後続の計算でデータ例外(S0C7アベンドなど)を引き起こす。

💡 怖くないための対策

レガシーシステムの移行や新規開発で `STRING` 関数を使うときは、基本的には「構造体内の全メンバが `CHAR` 属性(文字データ)で構成されている場合」に限定して使うのが最も安全で、バグを生みにくい黄金律です。

もし数値やフラグが混ざる場合は、メモリレイアウトのバイトオフセットを厳密に計算するか、あるいは安全なMOVE処理(PL/Iであれば `ASSIGN` や個別の代入)を心がけましょう。

—

まとめ

いかがでしたでしょうか?

  • PL/Iは予約語の縛りが緩く、柔軟に書ける言語であること
  • 構造体はメモリ上で綺麗に連続して配置されていること
  • `STRING` 関数を使えば、構造体全体をひとまとめの文字列として自由自在に扱えること
  • ただし、数値型が混ざる場合のメモリ解釈には十分注意すること

これさえ押さえておけば、レガシーシステムの電文処理やファイルレイアウト変換のコードに出会っても、もうビクともしません。「あ、あの構造体を丸ごと文字として扱っているんだな」と、コードの意図がスッと見えてくるはずです。

日々のメインフレーム保守やマイグレーション作業、大変なこともあるかと思いますが、一つずつ紐解いていけばPL/Iはとても合理的で面白い言語です。応援しています!

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