gnuplotを用いたライブラリをC言語で作ってみた.

# ファイルからグラフを作成するライブラリ
最近数値計算をするプログラムをC言語で書く機会が多いので,
その処理をライブラリにして再利用できるようにしておこうと思ったのがきっかけ.

## 機能
### 誤差計算
```calculate.c
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "calculate.h"

void calcurlate_error(const char *file, const char *file1, const char *file2){
    FILE *fp = fopen(file,"w");
    FILE *fp1 = fopen(file1,"r");
    FILE *fp2 = fopen(file2,"r");
    
    if(fp1 == NULL || fp2 == NULL){
        printf("file null error");
        return;
    }

    double x1,x2,y1,y2;
    double error;
    while(fscanf(fp1,"%lf,%lf",&x1,&y1) == 2 && fscanf(fp2,"%lf,%lf",&x2,&y2) == 2){
        if(fabs(x1 - x2) > 1e-6){
            printf("xの値が一致しません");
            break;
        }
        error = fabs(y1 - y2);
        printf("%8.3lf,%8.5lf\n",x1,error);
        fprintf(fp,"%8.3lf,%8.5lf\n",x1,error);
    }
    
    fclose(fp);
    fclose(fp1);
    fclose(fp2);
}
```
2つのファイルのデータを使って誤差を求めます.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536729/3f4a03b8-ca8d-ee93-1104-dfce809cef59.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536729/28d9c8b8-9492-9e60-cf98-0aa352489e67.png)

上記のようなデータを２つ用意して2列目の値を比較します.
使用例としては理論値と計算データを比較するときに用います.
### 1つのファイルのグラフを作成
```calculate.c
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "calculate.h"

int aFilePlotter(const char *file){
    FILE *gp;
    gp = popen("gnuplot -persist","w");
    fprintf(gp,"set multiplot\n");
    fprintf(gp,"set datafile separator ','");
    //fprintf(gp,"set xrange[0:2]\n");
    //fprintf(gp,"set yrange[0:0.6]\n");
    //fprintf(gp,"set xlabel 't'\n");
    //fprintf(gp,"set ylabel 'y(t)'\n");
    fprintf(gp,"plot '%s' with line\n",file);
    fprintf(gp,"quit\n");
    pclose(gp);
    
    return 0;
}
```
1つファイルを引数として渡すとグラフを作成してくれます.
### 複数のファイルのグラフを作成
```calculate.c
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "calculate.h"

int multiFilePlotter(int numFiles, ...)
{
    FILE *gp;
    gp = popen("gnuplot -persist","w");
    if(gp == NULL){
        fprintf(stderr, "Error: Could not open gnuplot\n");
        return -1;
    }

    fprintf(gp,"set multiplot\n");
    fprintf(gp,"set datafile separator ','");
    //fprintf(gp,"set xrange[0:2]\n");
    //fprintf(gp,"set yrange[0:0.6]\n");
    fprintf(gp,"set xlabel 't'\n");
    fprintf(gp,"set ylabel 'y(t)'\n");
    va_list args;
    va_start(args, numFiles);
    fprintf(gp, "plot ");
    for(int i = 0; i < numFiles; i++){
       const char *file = va_arg(args, const char*);
       fprintf(gp, "'%s' with lines", file);
       if(i < numFiles - 1){
        fprintf(gp, ", ");
       }
    }
    fprintf(gp,"\n"); 
    va_end(args);
    fprintf(gp,"quit\n");
    pclose(gp);

    return 0;
}

```
複数のファイルを用意して比較グラフを作成します.

## ライブラリ化
```
gcc -c calculate.c -o calculate.o
ar rcs libCalculate.a calculate.o
```
今回は静的ライブラリとして作成しました.
## 使い方
```test.c
#include <stdio.h>
#include "calculate.h"

int main(){
    multiFilePlotter(3,"euler.txt","runge.txt","answer.txt");
    return 0;
}
```
```
gcc test.c -L. -lcalculate -o test
./test
```
```-L.```オプションはライブラリが存在するディレクトリを示す必要があります.
L.では現在のディレクトリを指定していることになります.

## 感想
ライブラリとか作ったことなかったので,完成はしましたが細かいことはよくわかってません.
静的ライブラリと動的ライブラリの違いや,コマンドの詳細な意味(例えば-lcalculateは自由につけれる名前で適当につけたが,使う場面がないからなぜ名付けたかわかってない.)などがわかっていません.
これで多少はコーディングが楽になることを祈ります.
githubとかで一般公開してもいいですが,面倒なので今はしません.
それと,今回のライブラリはライブラリといえるほど汎用性が高くありません.
例外処理がほとんど記載されていないのです.結構致命的.
具体的には***ファイルの中身の行数が同じ***じゃないといけなかったり,ラベルの指定ができなかったりと...かなり限定的な用途でしか用いることができないので公開はしません.
## 一言
プログラマーは楽をするために苦労する生き物だなぁ.
## 付録
ソースコード全部添付しときます.
それとおまけで作ったグラフを1つ添付.
```calculate.h
#ifndef CALCULATE_H
#define CALCULATE_H

void calculate_error(const char *file, const char *file1, const char *file2);
int aFilePlotter(const char *file);
int multiFilePlotter(int numFiles, ...);

#endif
```
```calculate.c
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "calculate.h"

void calcurlate_error(const char *file, const char *file1, const char *file2){
    FILE *fp = fopen(file,"w");
    FILE *fp1 = fopen(file1,"r");
    FILE *fp2 = fopen(file2,"r");
    
    if(fp1 == NULL || fp2 == NULL){
        printf("file null error");
        return;
    }

    double x1,x2,y1,y2;
    double error;
    while(fscanf(fp1,"%lf,%lf",&x1,&y1) == 2 && fscanf(fp2,"%lf,%lf",&x2,&y2) == 2){
        if(fabs(x1 - x2) > 1e-6){
            printf("xの値が一致しません");
            break;
        }
        error = fabs(y1 - y2);
        printf("%8.3lf,%8.5lf\n",x1,error);
        fprintf(fp,"%8.3lf,%8.5lf\n",x1,error);
    }
    
    fclose(fp);
    fclose(fp1);
    fclose(fp2);
}

int aFilePlotter(const char *file){
    FILE *gp;
    gp = popen("gnuplot -persist","w");
    fprintf(gp,"set multiplot\n");
    fprintf(gp,"set datafile separator ','");
    //fprintf(gp,"set xrange[0:2]\n");
    //fprintf(gp,"set yrange[0:0.6]\n");
    fprintf(gp,"set xlabel 't'\n");
    fprintf(gp,"set ylabel 'y(t)'\n");
    fprintf(gp,"plot '%s' with line\n",file);
    fprintf(gp,"quit\n");
    pclose(gp);
    
    return 0;
}

int multiFilePlotter(int numFiles, ...)
{
    FILE *gp;
    gp = popen("gnuplot -persist","w");
    if(gp == NULL){
        fprintf(stderr, "Error: Could not open gnuplot\n");
        return -1;
    }

    fprintf(gp,"set multiplot\n");
    
    //fprintf(gp,"set xrange[0:2]\n");
    //fprintf(gp,"set yrange[0:0.6]\n");
    fprintf(gp,"set xlabel 't'\n");
    fprintf(gp,"set ylabel 'y(t)'\n");
    va_list args;
    va_start(args, numFiles);
    fprintf(gp, "plot ");
    for(int i = 0; i < numFiles; i++){
       const char *file = va_arg(args, const char*);
       fprintf(gp, "'%s' with lines", file);
       if(i < numFiles - 1){
        fprintf(gp, ", ");
       }
    }
    fprintf(gp,"\n"); 
    va_end(args);
    fprintf(gp,"quit\n");
    pclose(gp);

    return 0;
}

```
ライブラリを用いて作成したグラフ.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536729/982f2731-56af-81d2-e9ab-6472efae94e9.png)