【入門編】Javaリプレイスにおける文字列型のマッピング課題 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの深い森へようこそ。システムアーキテクトの私です。

JavaやC#、あるいは今風の言語をバリバリ書いてきた方にとって、IBMメインフレームの世界は、時として「謎の古代遺跡」のように映るかもしれませんね。特に、そこで長く生き抜いてきた「PL/I(ピーエルワン)」という言語に出会ったとき、「なんだこの変な書き方は……!」とフリーズしてしまうエンジニアを、私は数え切れないほど見てきました。

「他の言語の経験はあるけれど、メインフレームやPL/Iは初めて……」という方、どうぞご安心ください。怖がる必要はまったくありません。今回は、Javaへのリプレイス案件で誰もが最初に直面する「文字列型のマッピング地獄(パディングとエンコーディング)」について、現場の知見を交えて優しく紐解いていきましょう。

—

1. PL/Iの文字列って、JavaのStringとなにが違うの?

Javaの世界では、文字列を格納する `String` はとても賢くて柔軟です。文字数が増えれば勝手にメモリを伸ばしてくれるし、末尾の空白(スペース)なんて意識することも基本的にはありませんよね。

しかし、PL/I(そしてメインフレームの世界)は「決まった箱に、決まった行儀でデータを詰める」という厳格な美学を持っています。

ここで、PL/Iの代表的な文字列定義を見てみましょう。

/ 10バイトの固定長文字列(CHARACTER VARYINGではありません) /
DCL 顧客氏名 CHAR(10) INIT(‘山田’);

この `CHAR(10)` という宣言、Javaの感覚だと「最大10文字入る箱」のよう思えますが、実は「問答無用で常に10バイトを占有する固定長の箱」なんです。

ここに `’山田’` という2文字を入れると、PL/Iのruntimeは空いた8バイト分をどうするか?
そう、自動的に半角スペースで埋め尽くします(これを「ライト・パディング」と呼びます)。

  • メモリ上の実態:`’山田 ‘` (山田+スペース8個)

Javaにこのデータをそのまま持ってくると、`”山田 “` という謎のスペースだらけの文字列になってしまい、画面表示やDB検索で思わぬバグを引き起こす原因になります。これが、リプレイス時の最初のトラップです。

—

2. 予約語を持たないPL/Iの優しさと、それが招くカオス

ちょっと余談ですが、PL/Iの面白い(そして恐ろしい)特徴として「PL/Iには厳密な予約語(Keyword)が存在しない」という仕様があります。

どういうことかと言うと、`IF` や `CHAR` さえも、プログラマが変数名として使えてしまうのです。

/ これは合法的な(しかし絶対にやってはいけない)コードです /
DCL CHAR CHAR(10);

コンパイラは前後の文脈を必死に読み取って、「あ、この `CHAR` はデータ型の宣言だな」「こっちの `CHAR` は変数名だな」と健気に判断しています。
JavaやCOBOLに慣れた頭からすると「なんて自由奔放なんだ……!」と驚愕しますが、マイグレーションの際はこの柔軟すぎる文法がパース(構文解析)の壁になることも。でも、自分でコードを書くときは「変数名に予約語を気にしなくていいんだな」と、気楽に構えていて大丈夫です。

—

3. Javaリプレイスにおける最大の壁:パディングとエンコーディング

話を文字列マッピングに戻しましょう。
PL/Iの固定長文字列(`CHAR(n)`)をJavaへ移行する際、私たちは主に2つの壁を越えなければなりません。

壁①:末尾スペースのトリミング(パディングの処理)

先ほど見たように、PL/Iの `CHAR(10)` に格納されたデータは、有効文字の後ろがすべてスペースで埋められています。
Java側に移行する際は、通常、ビジネスロジック層やDTO(Data Transfer Object)へのマッピング時に、末尾の空白を削る(`trim()` する)処理を入れる必要があります。

壁②:文字コード(EBCDIC と ASCII/UTF-8)の呪縛

これが一番厄介です。メインフレームで使われる文字コードは、通常 EBCDIC(漢字の場合はJEFやIBM漢字など) です。
一方、現代のJava(LinuxやWindows上のJVM)が愛するのは UTF-8 や Shift_JIS です。

単にファイルをFTPなどでJava側に持ってきて `new String(bytes)` とやっても、文字化けの祭典が開催されるだけです。メインフレームのバイナリデータをJavaで正しく扱うには、適切な文字コード変換(Charset指定)が不可欠となります。

—

4. 【実践】JavaでPL/Iの固定長文字列を優しく受け止めるコード例

百聞は一見に如かず。PL/I側から出力された固定長バイナリ(あるいはファイル)を、Java側で安全に受け取り、パディング(空白)を綺麗に処理するサンプルコードを見てみましょう。

import java.nio.charset.Charset;

public class PliStringMapper {

// メインフレーム特有のEBCDIC文字コード(IBM-930など、環境に合わせて変更してください)
private static final Charset EBCDIC_JP = Charset.forName(“IBM-930”);

/

  • PL/IのCHAR(n)領域からバイト配列を受け取り、JavaのStringに安全に変換する
  • @param rawBytes メインフレームから渡された固定長バイト配列
  • @return 末尾のパディング(スペース)を除去したスッキリしたString

/
public static String convertPliCharToString(byte[] rawBytes) {
// 1. まずEBCDICからJavaのUnicode(UTF-16等)へ変換
String rawString = new String(rawBytes, EBCDIC_JP);

// 2. PL/I特有のライト・パディング(末尾の全角・半角スペース)を除去する
// ※メインフレームでは全角スペース ‘\u3000’ がパディングに使われることもあります!
return trimRight(rawString);
}

/

  • 文字列の末尾にある半角・全角スペースを切り捨てるヘルパーメソッド

/
private static String trimRight(String value) {
if (value == null) {
return “”;
}
// 正規表現を使って、末尾の半角スペース(\u0020)と全角スペース(\u3000)を綺麗に消し去ります
return value.replaceAll(“[\\u0020\\u3000]+$”, “”);
}

// — 実行テスト用メインメソッド —
public static void main(String[] args) {
// 模擬データ:PL/Iの CHAR(10) が出力したバイト列をイメージ
// (実際にはEBCDICですが、ここでは分かりやすく説明用文字列で代用)
String simulatedPliField = “山田 “; // 山田+スペース6個

System.out.println(“変換前(長さ: ” + simulatedPliField.length() + “): [” + simulatedPliField + “]”);

String cleanName = trimRight(simulatedPliField);

System.out.println(“変換後(長さ: ” + cleanName.length() + “): [” + cleanName + “]”);
// 出力結果: 変換後(長さ: 2): [山田]
}
}

このように、データの構造と「向こう側のルール(パディングと文字コード)」さえ分かってしまえば、Javaへのリプレイスは決して怖くありません。

—

まとめ

今回は、PL/Iの基本とJavaリプレイスにおける文字列マッピングの課題についてお話ししました。

  • PL/Iの `CHAR(n)` は、常に指定サイズ分の箱を確保し、余った部分はスペースで埋める(パディングする)。
  • Java側で受け取る際は、文字コード(EBCDICなど)の考慮と、末尾スペースのトリミングが必須。
  • PL/Iには予約語がないという自由すぎる一面もあるが、基本のデータ構造はとてもシンプル。

レガシーシステムの移行は、古い時代の先人たちの知恵や制約を一つずつ紐解いていく、まるで考古学のような面白さがあります。「なんだか難しそう」と感じていた仕様も、こうして分解してみれば、私たちが普段書いているコードとなんら変わりません。

基幹システムのモダナイゼーションに挑むあなたの背中を、これからもそっと、そして力強く押していければと思います。それではまた、次の現場でお会いしましょう!

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