設定ファイル読み込み時の工夫
C 版作成は 1991 の秋
数値計算のプログラムを組んで研究する場合、
ということが多いと思います。条件を変える度に
プログラムを書き換えてコンパイルし直すのは大変
なので、計算の条件は設定ファイルに書き、その
ファイルを読み込む形式にするのが普通です。
ここで、設定ファイルを次のような
形式で作ると便利です。
-----------------------------------------
# bpm parameter
1 1 1.5
λ dz/λ dn0
# geometry lattice
-20 20 201
-10 10 101
1 行目が xmin xmax nx
2 行目が ymin ymax ny
-----------------------------------------
「見出し」と「データ」という
形式になっています。この例では # で始まる行が
見出しで、見出し以降の 1 〜 2 行がデータです。
このファイルからデータを読み込むには
- ファイルを先頭まで巻き戻す
- 読み込みたいデータに対応する見出しの次の行まで読み取りポインタを進める
- データを読む
という作業が必要です。1. と 2. の部分を関数に任せます。
従って、データを読み込む部分は C の場合、
-----------------------------------------
find_header(fp,"# bpm parameter");
fscanf("%lf%lf%lf",&ramuda,&dz,&dn0);
find_header(fp,"# geometry lattice");
fscanf("%lf%lf%d",&xmin,&xmax,&nx);
fscanf("%lf%lf%d",&ymin,&ymax,&yx);
-----------------------------------------
のようになります。一方、ヘッダを見つける関数 find_header() は
/*-------------------- ヘッダーを見つける -----------------------*/
int find_header(FILE *fp, char keyword[])
/*
入力 fp ファイルポインタ
keyword キーワード
戻り値 1 : ヘッダを見つけた 0 ヘッダが見つからなかった
*/
{
char char_buf[256];
char keyword_buf[256];
char *condition;
int len,i;
strcpy(keyword_buf,keyword);
rm_right_space(keyword_buf); /* 右側の空白を取り除く */
rewind(fp);
for(;;){
condition = fgets(char_buf,BUFSIZ,fp);
if ( condition == NULL ) break;
rm_right_space(char_buf); /* 右側の空白と改行記号を取り除く */
len = strlen(char_buf);
if ( len == 0 ) continue; /* この文はなくてもよい */
for ( i = 0 ; i < len ; i++ ){ /* 左側の空白を取り除く */
if ( char_buf[i] != ' ' ) break;
}
if ( strcmp(&char_buf[i],keyword_buf)==0){
return(1);
break;
}
}
fprintf(stderr,"cannot find header. key word : |%s|\n",keyword);
return(0);
}
/* 文字列の右側の空白を削除する 改行記号がある場合はそれも除去する */
rm_right_space(str)
char str[];
{
int i;
i = strlen(str)-1;
if ( str[i] == '\n' ) i--;
while(i>=0){
if ( str[i] != ' ') break;
i--;
}
str[i+1] = '\0';
}
-----------------------------------------
のようになります。この方法の長所は以下の通りです。
- データと次のヘッダの間は何を書いても良い
- 新たなヘッダを導入した時、プログラムの
データ読み込み部を工夫することにより、古い設定ファイル
を修正する必要が無い。つまり、
「ヘッダが見つからない場合はデフォルトの値を入
れる」ようにプログラムを作ることが出来る。
- 幾つかのデータを試すのが容易
1. は次のような例で説明できます。
-----------------------------------------
# bpm parameter
1 1 1.5
λ dz/λ dn0
この部分に色々と注釈を書くことが出来ます
# geometry lattice
-20 20 201
-10 10 101
-----------------------------------------
3. について : 例えば次のようにいくつかの条件を
入れ換えて試すことが出来ます。このとき、
前回使った条件をファイルの中に残しておくことが
出来ます。
-----------------------------------------
# bpm parameter
1 1 1.5 <---+
λ dz/λ dn0 |
+--- 入れ換える
2 1 1.6 |
λ dz/λ dn0 <---+
-----------------------------------------
このように、設定ファイルの形式を工夫する
ことにより、計算条件の管理が楽になります。
ここでは最も単純な例を挙げましたが、数値を読み込む部分を fscanf() を
使うのではなく fget_double(fp,&a) のように専用の関数を呼ぶことにし、
その関数の中で「";" 以降はコメントとみなす」という処理を行うと、
より便利になります。私がふだん使っているヘッダ読み込み用の
関数群 fhead.c を示します。
find_header(fp,"mojiretu") を実行することにより、ファイルポインタの
位置が移動すると困る場合は、
-----------------------------------------
long file_loc;
file_loc = ftell(fp); /* ファイル位置を記憶 */
fp を移動させる何らかの処理
fseek(fp,file_loc,SEEK_SET); /* ファイル位置を元に戻す */
-----------------------------------------
のように書けばよろしい。
C++ 版が fhead.h と fhead.cpp、
fortran 版が fhead.f、
perl 版が fhead.pl 、
python 版が fhead.py です。
fortran, perl, python 版は ";" から始まるコメントを処理する機能はありません。
fortran では read(idev,*) を 1 回実行するごとに改行を行うので
-----------------------------------------
# sample data
1 2 各行につき 2 つの数値を read する
2 4 ここにコメントを書いてもよい
-----------------------------------------
のように読み取るデータの個数が決まっている時は、
その右側にコメントを書くことも出来ます。
私は自分のホームページで公開している nsgraph というグラフ描画
ツールを使っていますが、それと組み合わせると次のような
ことが出来ます。例えば、光導波路の計算には、入射端から
入れる光の強度分布をまず計算して求めておく必要があります。
入射光の強度分布を表すファイルを次のような形式で作ります。
-----------------------------------------
# optical field
0 0
1 1
2 3
.....
.....
100 0
空行
a
gl
c
-----------------------------------------
nsgraph にとっては # はコメント行を表し
空行は無視しますので、上記のファイルを nsgraph に
与えると、フィールド分布をグラフに表します。
'a' 'gl' 'c' は nsgraph の命令です。一方、導波路の
伝搬問題を解析するプログラムは上の # optical field を
ヘッダとして探し、ヘッダの次の行から空行までを
データとして読み込みます。1 つの
ファイルに 2 つの意味を持たせることが出来ます。
このように、ほんの少しの工夫をすることで、
とても便利になります。このヘッダ検索システムは
私が修士のときから愛用している方法です。