4 C言語からgnuplotを操作する

4.1 パイプとは

gnuplotをC言語のプログラムから制御するには,パイプを使うのが最も簡単である.ここ では,C言語のプログラムによりパイプの生成しgnuplotを起動を行い,パイプを通してコ マンドを送る.C言語のプログラム内にコマンドを記述することにより,プログラマーの 意図したとおりにgnuplotを操作することができる.

接続方法を説明する前に,UNIXのパイプという機能を簡単に説明しておく.UNIXのコマン ドの大部分は,標準入力(キーボード)からデータを受け取り,標準出力(ディスプレイ) に処理した結果を出力するようになっている.例えば,


	ls -l

などである.このように,コマンドをフィルターと呼ぶ.

複数のフィルターコマンドを接続して,かなり複雑な処理ができる.最初のコマンドに処 理すべきデータを与え,その標準出力を次のコマンドに渡すのである.例えば,


	ls -l | sort -n -k +5

のようにする.最初のコマンドで,カレントディレクトリーのファイルとディレ クトリーの情報を調べ,次のコマンドでファイル容量の順に並べている.「ls -l」の出力が 「sort -n -k +5」の入力になってる3

このようにコマンドを連結する機能をパイプラインという.そして,連結する | を パイプという.あたかも,パイプにデータが流れているかのようである.もちろん,2個 以上のコマンドの連結が可能である.このようにパイプを使ってコマンドをつなぐことに より,UNIXではかなり複雑な動作も簡単に記述できる.

4.2 パイプによるgnuplotへのコマンド送信

パイプを使うことにより,C言語のプログラムを通してgnuplotを制御することができる. C言語のプログラムから,gnuplotにパイプを通してコマンドを流し,プログラマーの思 い通りに動作させるのである.これを実現するためには,(1)パイプを開く(2)パイプ を通してコマンドを送る(3)パイプを閉じる--という一連の操作が必要である.

パイプを開くためには,ファイルポインターをつかう.そのためファイルポインターを格 納する変数を用意しなくてはならない.パイプの先もファイルとして扱われるのである.


FILE *hoge;

次にgnuplotを立ち上げて,そこにパイプを接続する必要がある.次のようにする.


hoge = popen("gnuplot -persist","w");
popen()関数がパイプを開く命令である.これで,gnuplot が立ち上がり,パイプを 通して,コマンドを送ることができる.オプションのpersistで,gnuplotが終了し てもグラフが残るようにしている.そうしないと,コンピューターの動作は高速なので, gnuplotは一瞬にして終了し,グラフが消えてしまい,ほとんど動作内容が分からなく なる.popen()関数の戻り値はパイプの情報を示すファイルポインターである.この ファイルポインターを指定して,コマンドを送ることになる.以前,学習したファイル操 作とほとんど同じである.

パイプを通して,gnuplotにコマンドを送るのはfprintf()関数を使う.


fprintf(hoge, "plot sin(x)\n");
このfprintfを使って,gnuplotにいくらでもコマンドを送ることができる.あたか も,C言語の向こう側でgnuplotが立ち上がって,それから命令を送っているかのように動 作する.このようなことができるのが,コマンドを打ち込むCharacter-based User Interface(CUI)の良いところである.

すべての動作が終了したならば,パイプを閉じなくてはならない.これも,ファイルの操 作と全く同じである.


pclose(hoge);

4.3 プログラム例

4.3.1 グラフ作成

C言語からgnuplotへパイプを使ってコマンドを送る方法を示す.リスト1 はその例で,三角関数のグラフを描いている.パイプを開きfprintf()関数を使っ て,直接gnuplotにコマンドを送っているだけである.
   1 #include <stdio.h>
   2 
   3 int main(void){
   4   FILE *gp;
   5 
   6   gp = popen("gnuplot -persist","w");
   7   fprintf(gp, "plot sin(x)\n");
   8 
   9   pclose(gp);
  10 
  11   return 0;
  12 }

4.3.2 配列のデータをグラフ化

数値計算の場合,配列にデータが格納されることが多い.配列に格納されたデータを gnuplotに送には,次のようにする.複数のデータがある場合は,付録に載せている.
  1. まずは,配列のデータを送る準備が必要である.21行目のように,
    plot '-' [オプション]
    とする.
  2. データの送信は,24行目にように
    fprintf(パイプを表すファイルポインター,データの並び)
    とする.
  3. eを送れば,データの終了となる.27行目にように
    fprintf(パイプを表すファイルポインター,"e\n")
    とする.

   1 #include <stdio.h>
   2 #include <math.h>
   3 #define NX 720
   4 
   5 int main(void){
   6   FILE *gp;
   7   int i;
   8   double dx, x[NX+1], y[NX+1];
   9 
  10   /* ---- データ作成 ---- */
  11   dx=4*M_PI/NX;
  12   for(i=0; i<=NX; i++){
  13     x[i]=-2*M_PI+i*dx;
  14     y[i]=sin(x[i]);
  15   }
  16 
  17   /* ---- グラフ作成 ---- */
  18   gp = popen("gnuplot -persist","w");
  19   fprintf(gp, "set xrange [-6.5:6.5]\n");
  20   fprintf(gp, "set yrange [-1.5:1.5]\n");
  21   fprintf(gp, "plot '-' with lines linetype 1 title \"sin\"\n");
  22 
  23   for(i=0; i<=NX; i++){
  24     fprintf(gp,"%f\t%f\n", x[i], y[i]);    // データの書き込み
  25 
  26   }  
  27   fprintf(gp,"e\n");
  28 
  29   pclose(gp);
  30 
  31   return 0;
  32 }

4.3.3 データファイルのグラフ作成

複雑な数値計算を行う場合,データをファイルに保存してから,それをグラフ化すること もある.リスト3ではデータを一度ファイルに保存してから,それを呼び 出してグラフ化している.ファイルに格納されたデータをグラフにする方法は簡単で,27 行目のように
plot "ファイル名" [オプション]
と直接,ファイル名を指定指定したコマンドを送る.
   1 #include <stdio.h>
   2 #include <math.h>
   3 #define NX 720
   4 
   5 int main(void){
   6   FILE *data, *gp;
   7   char *data_file;
   8   int i;
   9   double dx, x, y;
  10 
  11   /*------ データファイル作成 ---------- */
  12   data_file="out.dat";
  13   data = fopen(data_file,"w");
  14 
  15   dx=4*M_PI/NX;
  16   for(i=0; i<=NX; i++){
  17     x=-2*M_PI+i*dx;
  18     y=sin(x);
  19     fprintf(data,"%f\t%f\n", x, y);
  20   }
  21   fclose(data);
  22 
  23   /*------ グラフの作成 ---------- */  
  24   gp = popen("gnuplot -persist","w");
  25   fprintf(gp, "set xrange [-6.5:6.5]\n");
  26   fprintf(gp, "set yrange [-1.5:1.5]\n");
  27   fprintf(gp, "plot \"%s\" with lines linetype 1 title \"sin\"\n",data_file);
  28   pclose(gp);
  29 
  30   return 0;
  31 }



ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年7月11日


no counter