【テクニカル・上級編】INDEXおよびVERIFY関数による文字列検索の最適化 – PL/Iの基本構文とデータ制御実践ガイド

PL/Iの「自由」という名の深淵:INDEXとVERIFYを極める文字列操作の作法

メインフレームの現場で、PL/Iの「予約語を持たない」という仕様に救われたことは何度あるだろうか。現代の言語であれば `INDEX` や `VERIFY` は当然のように予約語として保護されている。しかし、PL/Iにおいてはこれらは単なる組み込み関数に過ぎない。もし君がうっかり `DCL INDEX FIXED BIN(31);` と宣言してしまえば、コンパイラは即座にその関数を「変数」として解釈する。この柔軟性こそが、数十年稼働するレガシーコードの「魔法」であり、同時に移行担当者を絶望の淵へ叩き落とす「呪い」でもある。

今日は、文字列操作の基本である `INDEX` と `VERIFY` に焦点を当て、単なるマニュアルの解説を超えた、基幹システムアーキテクトとしての視座からその最適化と罠を紐解いていこう。

1. INDEXとVERIFY:使い分けの境界線

文字列検索において、我々はしばしば「どこにあるか(INDEX)」と「何が混じっているか(VERIFY)」を混同する。

  • INDEX(source, target): `source` の中に `target` が最初に現れる位置を返す。見つからなければ `0` だ。
  • VERIFY(source, pattern): `source` の中で、`pattern` に含まれない文字が最初に現れる位置を返す。これも見つからなければ `0` だ。

なぜVERIFYが重要なのか

データクレンジングの現場では、数値項目に不正な文字(空白や英字)が紛れ込むことが頻発する。`INDEX` で一つずつ文字を確認するような非効率なループを回してはならない。`VERIFY(DATA, ‘0123456789’)` と記述すれば、数値以外の文字が含まれる位置を一発で特定できる。

1
/ 不正な文字が含まれるかチェックする例 /
DCL DATA CHAR(10) INIT(‘12345A6789’);
DCL POS FIXED BIN(31);

POS = VERIFY(DATA, ‘0123456789’);
IF POS > 0 THEN DO;
/ ここでエラー処理。パックデシマル変換時のS0C7を未然に防ぐ /
PUT SKIP LIST(‘不正な文字を検出: 位置 ‘ || POS);
END;

2. NULLと空文字列の「エッジケース」という名の爆弾

Javaから来た若手が最も躓くのが、空文字列の扱いです。PL/Iにおいて `”`(長さ0)は非常に厄介だ。

  • INDEX(”, ‘A’): `0` を返す。
  • VERIFY(”, ‘ABC’): `0` を返す。

ここで肝心なのは、「対象文字列が空か?」というチェックを怠ると、後続のSUBSTR操作でアベンドするということだ。特にCICSオンライン画面から受け取ったCOMMAREAがパディングされた空白なのか、本当に空なのかを判別するロジックを組む際、`INDEX` の戻り値が `0` であることを「存在しない」とみなして処理を進めると、SUBSTRの引数エラー(S0C4や異常終了)を招く。必ず `LENGTH()` 関数との組み合わせでガードを固めること。

3. マイグレーション時の「暗黙の型変換」とパックデシマル

JavaやC#への移行時、最も恐ろしいのはPL/I特有の「暗黙の型変換」による精度の欠落だ。

PL/Iは `FIXED DECIMAL`(パックデシマル)をネイティブに扱うが、移行先のJavaでは `BigDecimal` を使用する。もしPL/I側で `INDEX` の結果を `FIXED BIN` に格納し、それをさらに `PIC S9(7)V99` と演算させると、コンパイラは最適化の過程でレジスタ間で符号ビットの反転を伴う変換を行うことがある。

特に、パックデシマルの内部符号が異常値(x’0F’以外)になっている場合に、算術演算で即座にアベンド(S0C7)する。文字列検索の結果をインデックスとして使用する際は、必ず `TRUNC` や `FIXED` 関数で型を明示的に固定し、不正な値が混入しないよう徹底的にバリデーションを行う必要がある。

4. 動的メモリ操作とポインタの活用

大規模バッチ処理では、`BASED` 変数と `POINTER` を駆使して、メモリを節約しながらスキャンを行うことが常套手段だ。

1
DCL BUFFER CHAR(32767) BASED(P_BUF);
DCL P_BUF POINTER;

/ 巨大なストリームを読み込みながら、特定の文字列を探す /
/ 毎回コピーを作るのではなく、ポインタをずらしながらINDEXを実行する /
DO WHILE(P_BUF -> BUFFER ^= ”);
/ ここで動的にアドレスを計算し、ポインタを更新 /
/ アーキテクトとしては、このポインタ演算の境界値チェックを忘れてはならない /
END;

ポインタ演算を用いると、不要なメモリコピーを排除でき、CPUのサイクル数を劇的に削減できる。ただし、ポインタの参照先がセグメント境界を跨ぐようなコーディングをすれば、それは即座にシステムダンプという名の「墓場」行きだ。

結論:コードは「生き物」である

PL/Iは、書き手が高い規律を持っていないと、容易に崩壊する。現代の言語のように「コンパイラが守ってくれる」ことを期待してはいけない。我々アーキテクトがやるべきは、コンパイラの挙動を逆手に取り、アベンドが発生する前に予兆を掴むロジックを組み込むことだ。

もし諸君が、現在稼働しているバッチのマイグレーションを担当しているのであれば、まずは徹底的に `VERIFY` を活用して入力データの健全性を担保することから始めてほしい。`INDEX` の戻り値一つにシステムの命運がかかっている——その緊張感こそが、メインフレーム・エンジニアの矜持である。

質問や、「こんな不可解なアベンドに遭遇した」といった事例があればいつでも聞かせてくれ。レガシーの深淵を共に覗き込もうではないか。

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