第4章  H仕様書とロケール
RPGプログラマーの為のC言語講座

RPGプログラマーの方も、H仕様書についてはふだん、あまり気に留めていないのではないでしょうか?
と言うことで、まずRPGのH仕様書について再度、確認しましょう!

H仕様書とは何でしょう

H仕様書はプログラムの生成や実行を制御します。

これだけでは、何のことかいまいち分からないですね
もう少し詳しく見てみましょう

H仕様書の主な項目は以下のようになります。

15桁目 デバッグ
18桁目 通貨記号
19桁目 日付形式
20桁目 日付の編集
21桁目 小数点の表記法
26桁目 代替照合順序
40桁目 符号処理
41桁目 用紙の位置合わせ
43桁目 ファイル変換
57桁目 透過性検査
75 〜 80桁目 プログラム識別コード

ところで、上記の通貨記号、日付形式、日付編集、小数点の表記方法などは、国が違うと 表記方法が変わる場合があるのは、皆さんもご存じだと思います。

たとえば、

通貨記号

日付形式

日付編集

小数点の表記

日本

YMD

.(ピリオド)

US

MDY

.(ピリオド)

その他に、フランスでは、小数点がカンマ','になります。

照合順序についても、

AaBbCc順
アイウエオ順
ほげほげ順

と、これも各国によって順序が違います。

RPGでは、これらの制御をH仕様書を変更することで、 PGMのロジックから切り離し、共通のPGMで国別サポートを可能にしようとしています。

それでも、いちいち、全PGMのH仕様書を書き換えてコンパイルするのは、大変ですね、
ですから、RPGでは、H仕様書を省略することが出来ます。

H仕様書を省略すると、QRPG/DFTHSPECというデータエリアの内容を コンパイラーは参照します。ユーザーごとに設定したいのであれば、RPGHSPECという データエリアを自分で作成してください。*LIBLにRPGHSPECがあれば、 こちらが優先です。

このデータエリアは長さが80桁で形式は、そのレイアウトはソースファイルにH仕様書を 記述する形式と同じです。

これにより、RPGは、上記データエリアの内容を変更して、プログラムをコンパイルする だけで一応、その国対応のPGMになることになっています。

まぁ、今で言うところの国際化対応ってことになるのでしょうか???

しかし、実際のアプリケーションは、もっと複雑な条件が存在するため、この機能だけで 国際化対応されているなんてとてもいえませんが、ただ、少なくても、限定された項目に ついての国別対応は可能といっていいでしょう。

前置きがかなり長くなりましたが、localeの話題に入りましょう。

まず発音ですが、ロケールとかローコルとかばれることが多いようです。

ロケールとは、ひとことで言うと、国際化PGMを作成するためのサポート機能

って感じでしょうか?

もちろん、プログラマーが、プログラム作成時にロケールを十分意識して作成することは必須です。

たとえば、ある文字が数字(0〜9)かどうかを判断する場合に

0xF0以上,0xF9以下

なんて条件で判別していたら、AS/400以外にもっていったときに正常に動く保証はありません。

ASCIIでは数字は0x30以上,0x39以下 ですし、他の16進をもつプラっトフォームもあるかもしれません。

isdigit関数を使用していれば、どのプラットフォームで実行しようと、正常に動くでしょう

	if (isdigit(x)){
		xは数字です
	}

ここまでひどくなくても、以下の場合はどうでしょう。

ある文字がアルファベットのA−Zかどうかを判断するのにPCでは

	if (('A' <= X && X <= 'Z') || ('a' <= X && X <= 'z')){
		Xはアルファベットです
	}

が成り立ちます。これは、PCの文字体系がA−Z,a−zに連続した16進数 を割り当てているからです。

でも、このPGMをAS/400で実行すると、正常にうごきません。

なぜなら、AS/400はEBCDIC文字体系を使用しており、EBCDICでは A−Z,a−zに連続した16進を割り当てていないからです。

もっと言えば、AS/400は、CCSIDをもっており、コードページが違うと、 同じEBCDICの文字でも異なる16進数が割り当てられる場合があります。

文字’a’の値がいつも0x81とはかぎりません。

これも、isalpha関数を使用していれば、正しく稼働します。

	if (isalpha(x)){
		Xはアルファベットです。
	}

日付/通貨についてはどうでしょう。

以下のプログラムを作成し実行してみてください

最初のプログラム LIST1

/*---------------------------------------------------------------*/
/* PROGRAM-ID  : LOCAL1                                          */
/* REMARKS     :ロケールって何?                                */
/* AUTHOR      : Y.Ide                                           */
/* DATE-WRITEN : 98/04/30                                        */
/* VERSION     : 01.00 ORIGINAL                                  */
/*---------------------------------------------------------------*/
/* ロケールって何でしょう                                        */
/* 日付や時刻、通貨記号の表現って国によってちがいますよね        */
/* RPGだとH仕様書で                                          */
/*   通貨記号(¥,$..)                                      */
/*   日付形式(YMD,MDY,DMY)                          */
/*   日付編集(/,)                                    */
/* が変更できます。                                              */
/*                                                               */
/* loaleを使うともっといろいろできます。                    */
/* PGMにlocaleを指定することで再コンパイルなしで        */
/* 表現方法の切り替えが可能です                                  */
/*                                                               */

#include<stdio.h>
#include<locale.h>
#include<time.h>
#include <QSYSINC/H/QSNAPI>

void main()
{
 char *string;
 struct lconv * mylocale;
 char timstr[70];      /*画面表示用*/
 int ch;
 time_t now;           /*システム時刻用のワーク*/
 struct tm *timeptr;
 double fp = 1.23; /* サンプルの数字*/

 now = time(NULL);          /*秒数形式の時刻*/
 timeptr = localtime(&now); /*時刻形式へ

/*DSM画面クリアAPIの実行*/
    QsnClrScl(_C_Get_Ssn_Handle(), '0', NULL);

/* 省略値のロケール */ 
 string = setlocale(LC_TOD, NULL);
 printf("%s",string);

/* US */ 
 string = setlocale(LC_ALL, LC_C_USA);
 mylocale = localeconv();
 printf( "\n");
 printf( "----------US----------\n");
 printf( "小数点%s %f\n", mylocale->decimal_point,fp);
 printf( "通貨%s\n", mylocale->currency_symbol);
 ch = strftime(timstr,sizeof(timstr)-1,"日付: %A,"
                " %b %d. \n 時刻: %I:%M %p", timeptr);
 printf("\n %s \n", timstr);

/*フランス*/ 
 string = setlocale(LC_ALL, LC_C_FRANCE);
 mylocale = localeconv();
 printf( "\n");
 printf( "----------フランス-----------\n");
 printf( "小数点%s %f\n", mylocale->decimal_point,fp);
 printf( "通貨%s\n", mylocale->currency_symbol);
 ch = strftime(timstr,sizeof(timstr)-1,"日付: %A,"
                " %b %d. \n 時刻: %I:%M %p", timeptr);
 printf("\n %s \n", timstr);

 /*ドイツ*/ 
 string = setlocale(LC_ALL, LC_C_GERMANY);
 mylocale = localeconv();
 printf( "\n");
 printf( "----------ドイツ----------\n");
 printf( "小数点%s %f\n", mylocale->decimal_point,fp);
 printf( "通貨%s\n", mylocale->currency_symbol);
 ch = strftime(timstr,sizeof(timstr)-1,"日付: %A,"
                " %b %d. \n 時刻: %I:%M %p", timeptr);
 printf("\n %s \n", timstr);

 /*UK*/ 
 string = setlocale(LC_ALL, LC_C_UK);
 mylocale = localeconv();
 printf( "\n");
 printf( "----------UK----------\n");
 printf( "小数点%s %f\n", mylocale->decimal_point,fp);
 printf( "通貨%s\n", mylocale->currency_symbol);
 ch = strftime(timstr,sizeof(timstr)-1,"日付: %A,"
                " %b %d. \n 時刻: %I:%M %p", timeptr);
 printf("\n %s \n", timstr);

 /*スペイン*/ 
 string = setlocale(LC_ALL, LC_C_SPAIN);
 mylocale = localeconv();
 printf( "\n");
 printf( "----------スペイン----------\n");
 printf( "小数点%s %f\n", mylocale->decimal_point,fp);
 printf( "通貨%s\n", mylocale->currency_symbol);
 ch = strftime(timstr,sizeof(timstr)-1,"日付: %A,"
                " %b %d. \n 時刻: %I:%M %p", timeptr);
 printf("\n %s \n", timstr);
}

以下のような、実行結果になるでしょう


  日付 : Sunday, Aug 16.              
  時刻 : 11:27 AM                     
                                        
---------- US ----------            
 小数点 .                             
 通貨 $                               
                                      
  日付 : Sunday, Aug 16.              
  時刻 : 11:27 AM                     
                                         
---------- フランス -----------       
 小数点 ,                             
 通貨 F                               
                                       
  日付 : Dimanche, 8 16.              
  時刻 : 11:27 am
 ---------- ドイツ ----------    
  小数点 ,                       
  通貨 DM                        
                                 
   日付 : Sonntag, Aug 16.       
   時刻 : 11:27 am               
                                 
 ---------- UK ----------      
  小数点 .                       
  通貨 $                         
                                 
   日付 : Sunday, Aug 16.        
   時刻 : 11:27 am               
                                 
 ---------- スペイン ----------  
  小数点 ,                       
  通貨 Pts   
  日付 : Domingo, AGO 16.                                            
  時刻 : 11:27 am
                                                    
 端末セッションを終了するためには,実行キーを押してください。

ロケールの変更だけで、同じプログラム、同じ関数が異なった動作をしているのが ご理解いただけたかと思います。

ロケールを変更して影響を受ける関数には、以下のようなものがあります

	   fprintf()
	   gmtime()
	   isalnum から isxdigit()
	   localeconv()
	   localtime()
	   mktime()
	   printf()
	   setlocale()
	   sprintf()
	   strcol()
	   strftime()
	   strxfrm()
	   time()
	   tolower()
	   toupper()
	   vfprintf()
	   vprintf()
	   vsprintf()

ロケールは以下のカテゴリーに分かれています。


LC_ALL
	プログラムの全ロケールを指定します。

LC_COLLATE
	strcoll および strxform 関数の動作に影響します。

LC_CTYPE
	文字操作関数の動作に影響します。

LC_MONETARY
	localeconv が返す貨幣情報に影響します。

LC_NUMERIC
	書式付き入出力関数および文字列変換関数の小数点文字、
	およびlocaleconv 関数が返す貨幣以外の書式指定情報に
	影響します。

LC_TIME
	strftime 関数の動作に影響します。

LC_TOD
	time 関数の動作に影響します。

上記カテゴリーを使ってロケールの何を変更したいのかを指定出来ます。

例)

setlocale(LC_TOD, NULL);はtime 関数の動作を省略値のロケールに設定します。

setlocale(LC_ALL, LC_C_USA);は全部のカテゴリーをUSAのロケールに設定します