秋の訪れとともにやって来たRPGの新機能、パート3
前々回と前回の記事(「あわせて読みたい記事」の欄を参照)では、今回のリリースで追加された新たなBIFおよび命令コード オプションについて取り上げました。今回の記事では、文字データの数値への変換に焦点を当てた新たな2つのコンパイラー オプションについて取り上げるつもりです。
これまでこれらの機能は必要なかったという方もおられるかもしれませんが、いずれ近いうちに必要となる可能性も高いかもしれません。なぜでしょうか。IBM i の世界でWebサービスの利用が急増しているからです。Webサービスをまだ提供または利用していない、あるいは近いうちにそうする予定もない、と言うクライアントには、過去12か月間、一人として会った記憶はないように思います。Webサービスを利用するときには、通常、Webサービスのデータは文字形式で送信されるため、常に文字から数値への変換を行う必要があります。
RPGには、こうしたタスクを処理するBIFが数多くあります。そのうち、最もよく使用されているのが、%DECと%INTです。これらの関数は、XML-INTOおよびDATA-INTOによって使用される数値変換の土台にもなっています。「不正な」数値データの処理の問題は、実際のところ、BIFの場合に比べて-INTO命令コードの場合の方が、解決が難しいようです。これらのBIFを使用した場合、文字ストリングをテストおよび操作して、変換しようとする前にそれらを「正当」にすることができます。これは、-INTO命令とともに使用するオプションでありません。-INTO命令は、単にエラーを出すだけです。
それでは、これらの新たなオプションが対処するのは、どのようなタイプの状況なのでしょうか。実は、2つあります。1つ目は、空のフィールドの処理に関するものであり、2つ目は、「12,345.67」というような数値の変換に関するものです。まったく標準的だと思われるでしょうか。では、「12.345,67」あるいは「12 345,67」についてはどうでしょうか。
北米地域にお住まいの方なら、ほぼ確実に1つ目が標準的であり、他の2つは少し変だと思われることでしょう。けれども、ヨーロッパを拠点としているか、私のように、ヨーロッパ生活が長い方なら、2つ目および3つ目の表記が標準的だと感じるのではないでしょうか。ヨーロッパ諸国のほとんどでは、小数点にコンマを、千の位の桁区切りにピリオドまたは空白を使用します(私のように、すぐにこのテーマについて調べたくなるという方には、こちらの記事 http://www.languageediting.com/format-numbers-eu-vs-us/ がお勧めです)。
こうしたフォーマットの多様性によってRPGの変換BIFの問題が引き起される理由は、RPGが、コンマとピリオドの 両方 を有効な小数点として扱うことによって、ずっとこうした違いに対応してきたからです。だからこそ、「12,345.67」は無効ではありません。RPGがコンマを理解しないからではなく、理解するからなのです。結果として、このストリングには2つの小数点が含まれているとRPGは考えます。
新たな2つのコンパイラー オプションは、CTL-OPTまたはH仕様書で式オプション(EXPROPT)のパラメーターとして実装されます。
*ALWBLANKNUMは、空のフィールドが0として処理されることを可能にします。これを指定していない場合、RPGは、空のストリングを数値に変換しようとする試みを、すべてエラーとして処理します。すべての変換BIFで、RPGは、先行、後続、さらには埋め込みブランクを常に適切に無視してきました。RPGは、完全に空のフィールドは大嫌いでした。実際の場面で私が初めてこれに遭遇したのは、0のときはおそらく数値がブランクであるだろうXMLドキュメントを処理しているときでした。
*USEDECEDITは、コンパイラーに、DECEDITキーワードによって指定された値を使用するように指示します。ほとんどのケースでは、これは実際、指定されていないため、デフォルトで*JOBRUNになり、結果的に、使用される値はQDECFMTシステム値が制御することになります。つづめて言えば、ほとんどの北米システムがデフォルトで小数点がピリオドになり、ヨーロッパ システムがデフォルトでコンマになるということです。これらが通常のQDECFMT設定であるためです。
以下は、これらのオプションの使用法を説明するシンプルなプログラムです。
Ctl-Opt ExprOpts(*AlwBlankNum : *UseDecEdit);
dcl-s charBlank char(10) Inz;
dcl-s charDecimal char(10) Inz(' 12,345.67');
Dcl-s value packed( 9: 2 );
Dcl-s quantity int(5);
quantity = %Int( charBlank );
value = %Dec( charDecimal: 9: 2 );
Dsply ( 'If the options work this is zero: ' + %Char(quantity) );
Dsply ( ' and this will be 12345.67: ' + %Char(value) );
Ctl-Opt行をコメント アウトした場合、どのオプションを削除したかに応じて、%INTまたは%DEC命令のいずれかに対して、RNQ0105「A character representation of a numeric value is in error(数値の文字表現にエラーがある)」というエラー メッセージが表示されます。
ここで、これらの特定の機能を実装するには、コンパイラーPTFに加えてランタイムPTFが必要であることを指摘しておきます。7.3および7.4で必要とされるPTF番号は、 https://www.ibm.com/support/pages/node/6342819で確認できます。
-INTO命令もこれらの変更によって影響を受けることを前述しました。また、動的配列およびFOR-EACHは、そのような状況で本領を発揮するとも述べました。そうしたことを踏まえて、これらの新機能のすべてを組み合わせた例を示して、この記事を終えようと思います。
まず、以下は、処理することになるXMLから抽出したものです。
<data>
<item>
<code>ABC</code>
<number>1</number>
<value>1,234.56</value>
</item>
<item>
<code>DEF</code>
<number> </number>
<value>78.9</value>
</item>
</data>
1つ目のitemインスタンスのvalueエントリーには、千の位の桁区切りが含まれ、2つ目のitemのnumber要素が空であることに注目してください。新オプションを指定しなかった場合、これらは両方とも、エラーを引き起こしていたでしょう。
以下は、ファイルを処理するコードです。
Ctl-Opt ExprOpts(*AlwBlankNum : *UseDecEdit);
dcl-ds item dim( *Auto: 100 ) Qualified;
code char(3);
number zoned(5);
value zoned(7:2);
End-Ds;
Dcl-ds entry likeDs(item);
Dcl-s file varchar(40) Inz('/home/paris/xmlstuff/play.xml');
xml-into item %xml(file : 'doc=file case=any' );
For-Each entry in item;
Dsply entry.code;
Dsply entry.number;
Dsply entry.value;
EndFor;
ご覧のように、新たな2つの変換オプション、動的配列、およびFOR-EACHループ バリアントを組み合わせて、ドキュメントを処理しています。また、簡略化されたフィールド参照と私が呼んでいるものも、このコードではっきり見ることができます。手作業でループをコーディングしていたとすれば、シンプルなentry.numberなどの参照は、item(element).numberとする必要があったでしょう。
前述したように、Ctl-Optの該当部分をコメント アウトすると、現在の挙動を確認することができます。XML-INTO命令は、RNQ0353「XML document does not match RPG variable(XML文書がRPG変数に一致しない)」というエラー メッセージを生成します。さらに詳しく調べると、理由コード8が表示され、識別されたフィールドをターゲット変数に割り当てることができないことが示されることが分かると思います。
終わりに
RPGに対する最新のアップデートについて、お分かりいただけたでしょうか。それらは私の大のお気に入りです。7.4をまだ導入していない場合、唯一不便な点は、7.4へ移行するまでは、これらの機能を完全には利用することができないことです。動的配列は7.3では利用可能でないからです。しかし、アップグレードすべき時だと上司に言うのにちょうど良い理由とならないでしょうか。