【入門編】マイグレーション時の罠:PL/Iの文字列とJavaのString/char配列 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの底知れぬパワーと、そこに眠るPL/Iという少し個性的な言語の世界へようこそ。

普段はJavaやC#、あるいはCOBOLなどのモダンな(あるいはビジネス主流の)言語をバリバリ書いている方にとって、メインフレームの世界へ足を踏み入れるのは、まるで「見たこともない外国の古い街に放り出されたような感覚」かもしれませんね。

「変数名のルールは?」「メモリの持ち方は?」——大丈夫です。怖がる必要は全くありません。今回は、Javaなどの他言語の常識が綺麗に裏切られる、しかしメインフレーム移行(マイグレーション)では避けて通れない「PL/Iの固定長文字列」と「JavaのString」の決定的な違いについて、そっと寄り添いながら紐解いていきましょう。

1. まずは安心してください:PL/Iには「予約語」の縛りがほぼ無い!?

JavaやCOBOLを触ってきた方なら、「あ、その単語は変数名に使っちゃいけないんだっけ(予約語)」というトラップに何度も引っかかった経験があるはずです。例えば、Javaで `int class = 10;` なんて書いたら、コンパイラに怒られてしまいますよね。

しかし、PL/Iはここで少し変わった優しい(あるいは油断ならない)顔を見せます。PL/Iには、いわゆる「絶対に変数名として使ってはいけない固定の予約語」というものが原則として存在しません。

例えば、PL/Iではこんな信じられない宣言が语法上、許されてしまいます。

1
/ 衝撃の変数宣言:キーワードをそのまま変数名にしちゃう世界 /
DECLARE IF CHARACTER(10);
DECLARE THEN FIXED BINARY(31);
DECLARE ELSE CHARACTER(5);

IF = ‘HELLO’;

「えっ、制御構文の `IF` や `THEN` を変数名にしちゃって、コンパイラはどうやって判断しているの?」と思いますよね。
PL/Iのコンパイラは、文脈(コンテキスト)を見て「あ、ここは代入先の変数だな」「ここは条件分岐の `IF` だな」と賢く空気を読んで解釈してくれます。

……とはいえ、実務の現場でこんなコードを書いたら、後からコードを読む同僚(あるいは数ヶ月後のあなた)が発狂してしまいます。「できるからといって、やっていいとは限らない」のがレガシー開発の鉄則。変数名には素直に意味のある名前をつけましょうね。

2. マイグレーションの最大級の罠:「CHARACTER(n)」という名の要塞

さて、本題のメモリ管理と文字列の話に移りましょう。ここからがJavaエンジニアが最もハマりやすい、メインフレームの深い森です。

Javaの `String` や `char[]` は、必要に応じて長さを変えたり、文字列の末尾に `\0`(NULL文字:ヌルターミネータ)を置くことで「ここまでが文字ですよ」とC言語風に教えたりしますよね。

しかし、PL/Iの代表的な文字列型である `CHARACTER(n)`(固定長文字列) は、そんな甘えを一切許しません。

固定長という名の「完璧な箱」

PL/Iの `CHARACTER(10)` を宣言すると、メモリ上に「問答無用でちょうど10バイトの箱」がガチッと確保されます。

  • もし「ABC」という3文字を入れたら?
  • 残りの7バイトには、自動的に「半角スペース(X’40’)」がみっちりパディング(埋め立て)されます。
  • メモリの中身:`A B C [ ] [ ] [ ] [ ] [ ] [ ] [ ]` (計10バイト)
  • もし10文字を超える「123456789012345」を入れようとしたら?
  • エラーで弾かれるわけではなく、容赦なく後ろがぶった切られて(切り捨てられて)、先頭の10文字だけが格納されます。

JavaのString感覚で移行すると起きる悲劇

この「自動スペース埋め」と「NULL終端(`\0`)が無い」という仕様が、JavaやC言語へ移行する際のバッファオーバーランや文字化けの温床になります。

Java側にデータを渡す際、PL/I側には「文字列の終わりを示す目印(`\0`)」がありません。そのため、もし何も考えずにJavaへそのままバイト配列を渡して文字列化しようとすると、Java側は「えっ、後ろに続くこの大量のスペースや、メモリのゴミは何!?」とパニックを起こしたり、予期せぬスペースだらけの文字列として扱われてしまいます。

3. 実践!PL/Iでの文字列定義と、移行時の注意点コード

百聞は一見にしかず。実際のPL/Iコードを見てみましょう。バッチ処理などでよく見かけるデータ構造の定義です。

1
/ ======================================================== /
/ PL/I サンプルプログラム: 顧客データの定義とパディングの挙動 /
/ ======================================================== /
CUSTOMER_INFO: PROCEDURE OPTIONS(MAIN);

/ 宣言部 /
/ 氏名:固定長20バイト(足りない分は右側にスペースが埋まります) /
DECLARE CUST_NAME CHARACTER(20);

/ 顧客コード:固定長5バイト /
DECLARE CUST_CODE CHARACTER(5);

/ ワークエリア /
DECLARE MSG_AREA CHARACTER(50);

/ データの代入 /
CUST_NAME = ‘山田 太郎’; / 4文字+漢字2文字=計8文字(EBCDIC等で12バイト程度?) /
/ 残りのバイトはすべてスペースでパディングされます! /

CUST_CODE = ‘A102’; / 4文字なので、残り1バイトはスペースになります /

/ 画面やログへの出力(PL/IのPUT SKIP) /
PUT SKIP EDIT (‘氏名[‘ , CUST_NAME , ‘]’) (A, A, A);
PUT SKIP EDIT (‘コード[‘, CUST_CODE , ‘]’) (A, A, A);

END CUSTOMER_INFO;

このコードを実行したとき、`CUST_NAME` の中身は「山田 太郎」の後ろに見えないスペースがギッシリ詰まった状態でメモリに存在しています。

Javaへ移行するときの処方箋

このデータをJavaへ引き渡すマイグレーションを行う場合、移行先のJavaプログラム側では必ず以下の処理が必要になります。

1. 末尾スペースのトリミング (`trim()`)
PL/Iから受け取った固定長文字列のパディング(スペース)は、Java側で `String.trim()` や `strip()` を使って綺麗に削ぎ落とす必要があります。これを忘れると、データベースの検索キーが一致しない(`’A102 ‘` と `’A102’` の不一致)という、夜中に冷や汗をかくバグの原因になります。
2. NULL終端の補正
C言語やJNI(Java Native Interface)を介してメインフレームのメモリ領域を直接読み書きする場合、PL/I文字列には `\0` が無いことを前提に、必要な長さ分だけ正確に切り出すか、Java側で安全に終端文字を付加するラッパーを書く必要があります。

おわりに:レガシーの仕様は「歴史のロマン」

いかがでしたでしょうか?
「変数名に `IF` が使える」という自由奔放さがある一方で、「メモリの長さを1バイトたりとも狂わせず、隙間なくスペースで埋め尽くす」というPL/Iの厳格な固定長管理。このギャップこそが、メインフレームの歴史の重みであり、面白さでもあります。

「文字列の長さが固定でカチッと決まっている」ということは、裏を返せば「メモリレイアウトが完全に予測可能であり、データ構造が絶対に崩れない」という強力なメリットでもあります。金融や基幹系システムで何十年も動き続けている信頼性は、こうした妥協のないデータ制御の積み重ねの上になり立っているのです。

マイグレーションのプロジェクトでPL/Iのコードに出会ったら、「うわ、古いな」と身構えるのではなく、「おっ、ここに綺麗に整列された固い箱があるな」と、優しく愛でてあげてくださいね。

あなたのレガシー移行の旅が、安全で実りあるものになりますように。それではまた!

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