Text=佐藤 尚 ソリューション・ラボ・ジャパン
本稿では、IBM iオペレーティングシステムのIFS APIを活用して、ILE RPGプログラムからテキストファイルを出力する方法について簡単に解説します。
IBM iの基幹システムからテキストファイルを出力する場合、通常はCPYTOIMPFコマンドを使うことが多いと思います。このコマンドはとても便利ですが、テキストファイルのレイアウトに合わせてDDSを用意する必要があり、出力レイアウトが単純な表ではないなど、DDSで表現できない形式だった場合、対応が難しくなります。
しかしIFS APIを活用すれば、自由なレイアウトでテキストファイルが出力できるため、比較的簡単に対応可能です。また、DDSの定義も必要なくなります。
オペレーティングシステムのAPIを活用する、というフレーズから、難しそうな印象を持つかもしれませんが、他のプログラミング言語(Visual Basic、PHPなど)でテキストファイルを出力する場合と同じような手続きでテキストファイルの出力が可能です。なのでそれらの言語での開発経験があれば、比較的簡単に利用できると思います。ぜひ、ご活用ください。
CPYTOIMPFコマンドを使用する場合
IBM iの基幹システムからCSVデータを出力する場合、CPYTOIMPFコマンドを使用することが多いでしょう。このコマンドは、単に実行するだけで、物理ファイルや論理ファイル内のデータをCSVファイルに変換してくれるので大変便利です。
ただし、必要なCSVファイルのフォーマットがデータベースファイルのフォーマットと異なったり、データの加工が必要だったりする場合は、CSVファイルのフォーマットに合わせた別の物理ファイルを用意し、データを一度そのファイルに格納してからコマンドを実行する必要があります。
他のプログラミング言語でのテキストファイル出力方法
一般的に使用されるプログラミング言語には、テキストファイルの入出力を行う機能が備わっているため、CSVファイルを出力する場合は、プログラムコードで任意の形式のCSVデータに加工して出力できます。
たとえばExcel VBAでは、次のように簡単に文字列をテキストファイルに出力できます。
CSVデータを出力したい場合は、言語の文字列編集機能を使用して、文字列をCSV形式に編集した上で出力することで、簡単に実現できます。
PHPで上記と同様の処理を行う場合は、次のようになります。VBAと同じくらい簡単です。
どちらのプログラムを実行した場合も、次のような内容のテキストファイルfruits.txtが作成されます(ただし文字コードは異なります)。
テキストファイルのレイアウトに合わせて、毎回DDSを作成するのは手間ですし、RPGプログラムでも同様にテキストファイルを出力できれば便利なのに、と感じたことはないでしょうか。
実はIBM iのIFS APIを利用すると、ILE RPGで上記プログラミング言語と同様の手順でテキストファイルを出力するプログラムを作成できます。言語の機能を利用するのではなく、システムAPIの呼び出しが必要なため、上記のプログラミング言語ほどコーディングが簡単ではありませんが、基本的な処理の流れや手続きは同じです。
ILE RPGでのテキストファイル出力方法
ILE RPGでは、オペレーティングシステムのIFS APIを利用することで、テキストファイルの入出力を実行できます。言語の機能ではなくシステムAPIの呼び出しなので、前述のVBAやPHPと比べると手続きがやや煩雑ですが、大まかな処理の流れは同じです。
次のILE RPGコードは、IFS(統合ファイルシステム)上にfruits.txtを出力しています。
以下に、ソースの各セクションについて説明します。
活動化グループの指定
IFS APIはプログラム(*PGM)ではなく、サービスプログラム(*SRVPGM)のプロシージャーとして提供されています。このため、静的プロシージャー呼び出しを有効にするために、プログラムの活動化グループを「*NEW活動化グループ」、または「名前付き活動化グループ」のいずれかに設定する必要があります。
プログラムの活動化グループは、CRTBNDRPGなどのプログラム作成コマンドのACTGRPパラメーターでも指定できますし、RPGのCtl-OptステートメントのACTGRPキーワードでも指定できます。
今回はCtl-OptステートメントのACTGRPキーワードで、*NEW活動化グループを指定しています。
IFS APIのプロトタイプ定義
IFS APIとして提供されるプロシージャーのパラメーターには、値渡しを必要とするものや、戻り値として戻されるものが多くあります。これらのタイプのパラメーターは、RPGに従来からあるCALLB、PLIST、PARM命令ではサポートされていないため、プロトタイプ呼び出しを使用する必要があります。
プロトタイプ呼び出しを行うには、呼び出すプロシージャーのプロトタイプをすべて、プログラムソース内に定義する必要がありますが、幸いなことに、ライブラリーQSYSINC内にIFS APIのプロトタイプが記述されたソースメンバー(ソースファイルQRPGLESRC内のメンバーIFS)が提供されているので、これを利用します。
そして以下のように、/COPYディレクティブを使用して上記メンバーを組み込みます(/COPYの位置に上記メンバーの内容が挿入されます)。
RDiを使用している場合、アウトラインビュー内に組み込んだプロトタイプや定数が表示されます。
そして、エディター上で入力補完が行われるようになります。
テキストファイルのオープン
テキストファイルにデータを書き込むには、まずファイルをオープンし、ファイル記述子(File Descriptor)と呼ばれる識別番号を取得する必要があります。ファイルをオープンするには、open APIを呼び出します。
open APIのプロトタイプ定義
次のソースコードは、/COPYディレクティブで組み込んだメンバー内のopen APIのRPGプロトタイプ定義です。このAPIは、5つのパラメーターと戻り値を持つプロシージャーであることがわかります。
詳細な解説は、以下のページにあります。
open()–Open File – IBM Documentation
次に、5つのパラメーターと戻り値について簡単に説明します。
パラメーター1 path
入力パラメーターです。オープンするファイルのパスが格納されたnull終了文字列へのポインターを指定します。こう書くと難しそうですが、プロトタイプ定義で「OPTIONS(*STRING)」が指定されているので、RPGの文字変数や文字リテラルをそのまま指定できます(null終了文字列への変換はシステムが自動的に実行します)。
パラメーター2 oflag
入力パラメーターです。このパラメーターは32ビット整数型で、ファイルに読み取り用、書き込み用、読み取り/書き込み用のどの方法でアクセスするかなどを対応するビットフラグをオンにして指定します。
たとえばファイルに読み取り専用でアクセスしたい場合は、右端のビットを1に(2進数の1=10進数の1を指定)、書き込み専用でアクセスしたい場合は右端から2番目のビットを1に(2進数の10=10進数の2を指定)、読み取り/書き込み用でアクセスしたい場合は右端から3番目のビットを1に(2進数の100=10進数の4を指定)します。
ただし、2や4などの数値リテラルをそのまま指定したのでは、あとでソースを読む人に意味が伝わりにくいので、プロトタイプと一緒に提供されている名前付き定数を利用します。よく使用されるのは、以下のとおりです。
パラメーター3 mode
入力パラメーターです。このパラメーターは符号なし整数型で、ファイルの作成時に使用するファイルのパーミッションビットを指定します。このパラメーターは、O_CREATEフラグが指定されている場合には必須です。
パラメーター4 conversion_id
入力パラメーターです。このパラメーターは符号なし整数型で、新しいファイルが作成されたときにファイルに関連付けられ、ファイル内のデータのCCSID(コードページ)となります。このパラメーターを指定するには、O_CCSIDまたはO_CODEPAGEフラグの指定が必要です。ファイルがすでに存在する場合は、このパラメーターの値は無視されます。
パラメーター5 text_file_creation_conversion_id
入力パラメーターです。このパラメーターは符号なし整数型で、RPGで処理されるデータのCCSID(コードページ)を指定します。O_TEXT_CERAT(加えてO_TEXTDATA、O_CREATE、O_CCSIDまたはO_CODEPAGE)フラグを指定した場合、このパラメーターの指定は必須です。
戻り値
戻り値は整数型で、ファイル記述子と呼ばれる識別番号が返されます。このファイル識別子を使用して、後続の処理でファイルへデータを書き込めます。ファイルのオープンに失敗した場合は、-1が返されます。
テキストファイルへの書き込み時のコーディング
文字コードUTF-8のテキストファイル「fruits.txt」を作成し、RPGの文字データを書き込む場合は、以下のようにコーディングします。
テキストファイルへの書き込み
write APIにファイル識別子と文字データを渡すことで、テキストファイルにデータを書き込めます。
write APIのプロトタイプ定義
以下のソースコードは、/COPYディレクティブで組み込んだメンバー内のwrite APIのRPGプロトタイプ定義です。このAPIは、3つのパラメーターと戻り値を持つプロシージャーであることがわかります。
パラメーター1 p1(file_descriptor)
入力パラメーターです。このパラメーターは整数型で、データを書き込むファイルのファイル記述子を指定します。
パラメーター2 p2(buf)
入力パラメーターです。このパラメーターはポインター型で、書き込むデータを含むRPG変数へのポインターを指定します。
パラメーター3 p3(nbyte)
入力パラメーターです。このパラメーターは符号なし整数型で、書き込むデータの長さをバイト数で指定します。
戻り値
書き込みに成功した場合、書き込んだデータのバイト数が返されます。この値はパラメーター3で指定した値以下になります。書き込みに失敗した場合は、-1が返されます。
テキストファイルへの書き込み時のコーディング
テキストファイルへ1行分のデータを書き込むには、write APIを使用します。以下のようにコーディングします。
write APIの最初のパラメーターには、open APIで取得したファイル記述子を指定します。
2番目のパラメーターには、書き込む文字データ変数へのポインターを指定します。変数へのポインターは、%Addr関数で取得できますが、Line変数は可変長文字列(VarChar)型のため、%Addr関数の2番目のパラメーターに、*Dataを指定する必要があります。
3番目のパラメーターには、書き込むデータの長さのバイト数を指定します。可変長文字列(VarChar)型変数の実際のデータの長さは、%Len関数で取得できます。
書き込む文字データには、行の終わりを示す改行コードを追加する必要があります。CRLFに対応するEBCDICのコードはX’0D25’です。
テキストファイルのクローズ
ファイルにすべてのデータを出力した後で、ファイルをクローズするには、close APIを使用します。以下のようにコーディングします。
エラーコードの取得
open APIで存在しないフォルダーパスを指定した場合などは、戻り値としてファイルオープンに失敗したことを示す-1が返されます。この時、getErrorPtr APIを呼び出して、エラーコードを取得できます。
このエラーコードはCPExxxxメッセージの下4桁番号に対応しているので、DSPMSGDコマンドを実行して、エラーの詳細を確認できます。
ILE RPGでCSVファイル出力
RPGでのテキストファイル出力方法がわかったので、物理ファイルの内容の一部をCSVファイルへ出力するプログラムを作成してみます。
データベースのレイアウト
物理ファイルZIPのDDSは、以下のとおりです。この物理ファイルには、日本郵便が公開している郵便番号データが格納されています。
この物理ファイルから、神奈川県のデータのみをCSVデータに出力します。さらに、出力するフィールドもZIPCODE、PREF、CITY、STREETの4つのみとします。
神奈川県のデータを選択するために、以下の論理ファイルZIPML1を使用します。
プログラムのコーディング
RPGプログラムのソースコードは、以下のようになります。
テキストデータ出力に関する変更点
ここからテキストデータ出力に関する変更点を説明します。
テキストファイルの削除
ファイルのCCSIDを確実に1208(UTF-8)にするために、テキストファイルをオープンする前にunlink APIを使用して削除しています。
unlink APIのプロトタイプは、以下のとおりです。
パラメーターは1つだけで、削除するファイルのパスが格納されたnull終了文字列変数へのポインターを渡す必要がありますが、OPTIONS(*STRING)が指定されているので、RPGの文字変数や文字リテラルをそのまま指定できます。削除に成功した場合は、戻り値に0、失敗した場合は戻り値に-1が返されます。
ローカル関数の追加
CSVデータ内の文字列は通常、ダブルクォーテーション(”)で囲みますが、これを文字列の連結演算子(+)を使用してコーディングすると、クォーテーションの数が多くなり、少々コードが読みにくくなります。
そこで、以下のようなローカル関数を定義してみます。
すると、コードが比較的読みやすくなります。
出力されたCSVデータ
出力されたzip_kanagawa.txtをPCにダウンロードし、テキストエディターで開くと、以下のように問題なく出力されていました。
・・・・・・・
今回は例としてCSVファイルを取り上げましたが、IFS APIを使用すると、データベースファイルのレイアウトに縛られずに、自由なレイアウトでテキストファイルを出力できることがおわかりいただけたと思います。
ただし、アプリケーションからシステムAPIを直接呼び出しているため、やはりコーディングの簡単さの点では、最初に挙げたVBAやPHPなどのプログラミング言語には及ばないのは確かです。
この点に関してはAPIのパラメーターを簡素化したラッパープロシージャーを作成し、サービスプログラムにまとめておくことで改善できると思います。
著者
佐藤 尚(さとう たかし)氏
ソリューション・ラボ・ジャパン株式会社
第1サービス事業部 第1サービス部
第4グループ マネージャー
IBM iユーザーの情報システム部門を経て、2006年にソリューション・ラボ・横浜(現・ソリューション・ラボ・ジャパン)株式会社に入社。主にIBM iを中心として、他のプラットフォームとの連携を行うシステムの設計・開発を行う。近年はお客様へのRPG研修サービスの講師を担当している。
[i Magazine・IS magazine]