設定ファイル読み込み時の工夫

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. 読み込みたいデータに対応する見出しの次の行まで読み取りポインタを進める
  3. データを読む

という作業が必要です。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. データと次のヘッダの間は何を書いても良い
  2. 新たなヘッダを導入した時、プログラムの データ読み込み部を工夫することにより、古い設定ファイル を修正する必要が無い。つまり、 「ヘッダが見つからない場合はデフォルトの値を入 れる」ようにプログラムを作ることが出来る。
  3. 幾つかのデータを試すのが容易
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.hfhead.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 つの意味を持たせることが出来ます。

このように、ほんの少しの工夫をすることで、 とても便利になります。このヘッダ検索システムは 私が修士のときから愛用している方法です。