こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がある方にとって、突然目の前に現れるPL/I(ピーエルアイ)のソースコードは、少し威圧的に感じられるかもしれませんよね。
「なんだか変数名のルールが独特だし、見慣れない関数がたくさんある……」
そんな風に不安になっていませんか?でも、大丈夫ですよ。一つずつ蓋を開けて中身を確認していけば、PL/Iほど合理的に作られた言語はありません。今回は、レガシー移行やバッチの改修現場で必ずと言っていいほどぶつかる「文字の長さ」と「メモリの大きさ」の謎について、一緒に紐解いていきましょう。
—
そもそもPL/Iの「識別子」と「予約語」って怖くない?
JavaやCOBOLには、たくさんの「予約語(IFやTHENなど、システムが特別な意味で使うから変数名に使えない言葉)」がありますよね。COBOLなんて予約語の多さに何度泣かされたことか……。
しかし、PL/Iの懐の深いところは、「厳密な意味での予約語を持たない」という設計思想にあります。
どういうことかと言うと、PL/Iでは `IF` や `READ` といったキーワードさえも、コンテキスト(文脈)から判断されるため、理論上は変数名として使えてしまうのです。(※もちろん、ソースコードが読みにくくなるので絶対にやめましょうね!)
この「柔軟だけど、時々お茶目すぎる」PL/Iの世界で、文字データを扱うときに誰もが一度はハマるのが、今回焦点を当てる `LENGTH` と `CURRENTSIZE` という2つのビルトイン関数(組み込み関数)の違いです。
—
LENGTH と CURRENTSIZE は何が違うの?
一言で違いを言うと、こうなります。
- `LENGTH` = 「論理長」(人間が見たときの文字の数・長さ)
- `CURRENTSIZE` = 「物理サイズ」(コンピュータがメモリ上で占有している実際のバイト数)
「え? 文字数とバイト数なんて、同じじゃないの?」と思ったそこのあなた。
実はここが、メインフレーム特有のデータ属性や、JavaやCOBOLからの移行組が最もツボにハマりやすいポイントなんです。
イメージしてみましょう:お弁当箱の例え
想像してみてください。あなたは今、スーパーでお弁当を買いました。
- お弁当に入っている実際の唐揚げの数(中身のデータ) $\rightarrow$ これが `LENGTH` です。
- 唐揚げを入れるために持ってきた「お弁当箱(メモリ領域)」の大きさ $\rightarrow$ これが `CURRENTSIZE` です。
お弁当箱がどれだけ大きくてスカスカであっても(最大サイズで宣言されていても)、中に入っている唐揚げが3個しかなければ、中身の数は「3個」ですよね。でも、バッグの中でそのお弁当箱が占めるスペース(物理サイズ)は、お弁当箱自体の大きさになります。
これをPL/Iのコードで見てみましょう。
—
実践!PL/Iコードで違いを体感しよう
レガシーなバッチプログラムを想定した、次のようなPL/Iのコード片を覗いてみてください。大文字ベースで記述するのがメインフレームの流儀です。
1
/ ————————————————– /
/ LENGTH と CURRENTSIZE の違いを学ぶサンプルプログラム /
/ ————————————————– /
DEMO_PGM: PROC OPTIONS(MAIN);
/ VARYING属性を持つ可変長文字列の宣言 /
/ 最大で50バイト入るお弁当箱を用意するイメージです /
DCL MSG_VAR CHAR(50) VARYING;
/ 固定的(固定長)な文字列の宣言 /
/ こちらは常に10バイトのスペースがガッチリ固定されます /
DCL MSG_FIX CHAR(10);
————————————————–
— 1. VARYING(可変長)のケース
————————————————–
MSG_VAR = ‘HELLO’; / 5文字を代入 /
/
- LENGTH(MSG_VAR) は「5」を返します。
- 今現在、変数に入っている文字の数(論理長)だからです。
/
PUT SKIP LIST (‘MSG_VARの論理長 (LENGTH): ‘ || LENGTH(MSG_VAR));
/
- CURRENTSIZE(MSG_VAR) は「52」を返すことが多いです。(環境により異なります)
- なぜ50文字なのに52なのか?
- 可変長文字列(VARYING)は、PL/Iの内部で「今の長さはいくつです」という
- 2バイトの「長さ情報(プレフィックス)」をデータの頭にオマケで持っているからです!
/
PUT SKIP LIST (‘MSG_VARの物理サイズ (CURRENTSIZE): ‘ || CURRENTSIZE(MSG_VAR));
————————————————–
— 2. 固定長(FIXED)のケース
————————————————–
MSG_FIX = ‘HELLO’; / 5文字を代入するが、宣言はCHAR(10) /
/
- 待ってください!CHAR(10)に’HELLO’(5文字)を入れた場合、
- 残りの5文字分はどうなると思いますか?
- PL/Iでは、足りない後ろの部分は自動的に「空白(スペース)」でパディング(埋め合わせ)されます。
- それでも LENGTH は「文字の有効な長さ」を返すため、ここでは「5」になります。
- (※末尾の空白をどう扱うかはビルトイン関数や代入ルールによります)
/
PUT SKIP LIST (‘MSG_FIXの論理長 (LENGTH): ‘ || LENGTH(MSG_FIX));
/
- 一方、CURRENTSIZE(MSG_FIX) はどうなるでしょうか?
- 固定長の場合、お弁当箱のサイズは最初から最後まで「10バイト」と決まっています。
- そのため、中身が何文字であれ、結果は常に「10」になります。
/
PUT SKIP LIST (‘MSG_FIXの物理サイズ (CURRENTSIZE): ‘ || CURRENTSIZE(MSG_FIX));
END DEMO_PGM;
—
マイグレーションや実務でどう使い分けるの?
現場でこの2つをどのように使い分けるべきか、シチュエーション別に整理しておきましょう。
1. 画面やファイルへ「書き出す文字数」を制御したいとき $\rightarrow$ `LENGTH` を使う
例えば、ログファイルに出力する文字列の長さを測ってループ処理を回したり、文字の切り出し位置を計算したりする場合は、人間が認識する文字数(論理長)が必要になります。そのため、`LENGTH` を採用します。
2. ファイルのI/Oバッファや構造体(人口構造体)のメモリサイズを計算したいとき $\rightarrow$ `CURRENTSIZE` (または `SIZE`)を使う
他システムへ電文(データ構造)を送信する際や、ストレージ上にバイナリとして書き出す際の「領域の確保サイズ」を厳密に知りたい場合は、メモリ上の物理バイト数が必要になります。そのため、`CURRENTSIZE` や `SIZE` を用います。
特に、PL/I特有の `VARYING` 属性(可変長データ)を扱うときは、先ほど解説した「データの頭にある2バイトの長さ制御部」の存在を忘れると、マイグレーション後のデータ長不一致バグ(いわゆる切り捨てエラーや領域オーバー)を引き起こす原因になります。ここが、JavaやCOBOL出身者が最もハマりやすい「レガシーの罠」ですので、ぜひ覚えておいてくださいね。
—
最後に:怖がらなくて大丈夫です
PL/Iの仕様書を見ると、難解な用語や数学的な定義が並んでいてクラクラしてしまうかもしれません。しかし、基本のデータ構造(固定長なのか、可変長なのか)と、コンピュータがメモリをどう使っているかのイメージ(お弁当箱の例え)さえ頭の中にあれば、怖いものはありません。
バッチ改修や移行作業で迷ったときは、いつでもこの記事を思い出してくださいね。
あなたのメインフレームライフが、実り多いものになるよう応援しています!
