後輩たちのためのEV3rt講座⑦(ファイルを扱う-fprintf)

#はじめに…#
ev3rtにてセンサーの値を取りたい時、よくやる手法はLCDに値を書く方法かと思いますが、これってあの小さい液晶を覗き込まなければならなく、見にくいですよね。
教育版ソフトウエアでは「データロギング」というモードがあり、モーター・センサーの値の動きを連続してみることができます。これに近いものをev3rtで実現できれば、ログ取りには困りませんよね。

ちなみにこれ↓がデータロギング
![スクリーンショット 2018-10-25 20.38.20.png](https://qiita-image-store.s3.amazonaws.com/0/183170/d70a94a1-abff-c0a3-8821-4eaae8c84696.png)


#標準Cライブラリを使用する#
ev3rtで使える標準Cライブラリには、ファイルを扱う機能があり、Windowsでいうメモ帳で読み書きできる.txtファイルも作成できます。よって今回は.txtファイルに値を書き込んでいくとします。

#書き方#
まずはサンプルコードを見てみましょう。

```app.c
void main_task(intptr_t unused)
{
    int i=100;

    FILE *file;//ファイルポインタを宣言
    file=fopen("/test.txt","a");//ファイルをオープン(名前の指定)
    fprintf(file,"Hello %d\n",i);//書き込み
    fclose(file);//ファイルを閉じる
}
```

fileというのはただの名前なので、別のファイルを作りたければfile2などとすれば複数個同時に作ることも可能です。その際は、上記例に示されているfileはすべてfile2に置き換えることになります。
(例)

```c
FILE *file;                    →  FILE *file2;
file=fopen("/test.txt","a");   →  file2=fopen("/test.txt","a");
fprintf(file,"Hello %d\n",i);  →　fprintf(file2,"Hello %d\n",i);
fclose(file);                  →  fclose(file2);
```

#テキストを確認#
プログラム終了後、ロボットとPCをUSBケーブルで繋ぐと、SDカード領域がマウントされるはずです。そしてその中に、先程指定した名前のファイルが作成されているはずです。無い場合はプログラムが正常に動いていません。もう一度やってみると作成される場合もあります。それでもなお作られない場合はプログラムミスを疑ってください。
今回の場合は「Hello 100」と書かれているはずです。

#fopenの書式#
上記例の`"a"`と書かれている部分ですが、これはテキストの処理方法を宣言していて、以下の表の様に様々な記述方法があります。ロボットの場合、値を随時書き足していくという使い方が多いと思いますので、オススメは`"a"`です。

| モード | 動作 | ファイルがあるとき | ファイルが無いとき |
|:-:|:-:|:-:|:-:|
| "r" | 読み込み専用 | 正常 | エラー(NULL)  |
| "w" | 書き込み専用 | サイズを0にする(上書き) | 新規作成 |
| "a" | 追加書き込み専用 | 最後に追加 | 新規作成 |
| "r+" | 読み込み(主)と書き込み | 正常 | エラー(NULL) |
| "w+" | 書き込み(主)と読み込み | サイズを0にする(上書き) | 新規作成 |
| "a+" | 読み込みと追加書き込み | 最後に追加 | 新規作成 |

#fprintfの書式#
##引数を用いる##
fprintfでは引数として任意の値を代入することができます。例えば、上記例ではint型変数testを`%d`の部分に動的に代入しています。この書式法を使えば、任意のタイミングの値を記録することが可能なのです。なお、値の型によって書式が異なります。

| 変換指定子 | 意味 |
|:-:|:-:|
| d , i | int 型の引数を 10 進符号付き整数に変換する |
| u | unsigned int 型の引数を 10 進符号無し整数に変換する |
| o | unsigned int 型の引数を 8 進符号無し整数に変換する |
| x , X | unsigned int 型の引数を 16 進符号無し整数に変換する |
| f , F | double 型の引数を小数形式浮動小数点数に変換する |
| e , E | double 型の引数を指数形式浮動小数点数に変換する |
| g , G | f か e のどちらかに変換する (変換の結果として得られる指数が -4 より小さいか，精度以上の場合は e 形式になる) |
| a , A | double 型の引数を 16 進浮動小数点に変換する |
| c | int 型の引数を 一端 unsigned char 型に変換し，変換結果の文字を書き込む |
| s | 文字配列の先頭要素へのポインタを文字列に変換する |
| p | void 型へのポインタを処理系定義の方法で表示文字の並びに変換する |
| n | 整数変数に出力済み文字数を格納する (引数は符号付き整数型へのポインタ) |

又、場合によっては長さ修飾子を付ける必要があります。

| 長さ修飾子 | 意味 |
|:-:|:-:|
| hh | 対応する実引数の型が char 型であることを指定する |
| h | 対応する実引数の型が short 型であることを指定する |
| l | 対応する実引数の型が long 型, wchar_t 型であることを指定する |
| ll | 対応する実引数の型が long long 型であることを指定する |
| j | 対応する実引数の型が intmax_t 型であることを指定する |
| z | 対応する実引数の型が size_t 型であることを指定する |
| t | 対応する実引数の型が ptrdiff_t 型であることを指定する |
| L | 対応する実引数の型が long double 型であることを指定する |

さらに、フラグというものを使うとさらに文字の表現が広がります。

| フラグ | 意味 |
|:-:|:-:|
| - | 変換結果をフィールド内に左詰めにする (デフォルトは右詰め) |
| + | 変換結果が正，負に関わらず符号を出力する (デフォルトは負の場合のみ出力) |
| 空白 | 変換結果の最初の文字が符号でない場合か，符号付き変換の結果が 0 の場合に空白を結果の前に付ける |
| # | 結果を "代替形式"※ に変換する |
| 0 | 出力文字数が最小フィールド幅未満の場合は 0 を埋める |

これだけ説明しといてなんですが、EV3rtで扱うような値(センサー値など)はint型が多く、計算でもしない限り`%d`だけで書き出せちゃうのです。(もちろん、私のように複数個の値でパーセンテージなどをだすとfloatもしくはdoubleになりますが…)

あ、そうそう。**printf**とか**fprintf**、あるいは**sprintf**などはすべて仲間みたいなもんなので書式もほぼ共通です。

ちなみに、この引数は複数個も可能です。

```app.c
void main_task(intptr_t unused)
{
    int i[10]={10,20,30,40,50,60,70,80,90,100};

    FILE *file;//ファイルポインタを宣言
    file=fopen("/test.txt","a");//ファイルをオープン(名前の指定)
    fprintf(file,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n"
,i[0],i[1],i[2],i[3],i[4],i[5],i[6],i[7],i[8],i[9]);//書き込み
    fclose(file);//ファイルを閉じる
}
```
この場合、出力は`10,20,30,40,50,60,70,80,90,100`になります。

###センサー値も代入可能###
以下のようにすれば、センサーの値も引数として代入可能です。（今回はライトセンサーをポート1、2に繋いでいます。）

```app.c
void main_task(intptr_t unused)
{
    int i=100;

    FILE *file;//ファイルポインタを宣言
    file=fopen("/test.txt","a");//ファイルをオープン(名前の指定)
    fprintf(file,"Port1:%d,Port2:%d\n",ev3_color_sensor_get_reflect(0),ev3_color_sensor_get_reflect(1));//書き込み
    fclose(file);//ファイルを閉じる
}
```
##改行する##
ファイルを作成するとき、書き出せたは良いものの、改行されていないと**非常**に見にくいです…
そこで「改行コード」を入力することで、文字列を次の行に移すことが可能です。
LINUX系(EV3rt含む)の場合は`\n`です。

#おまけ#
この作成したファイルですが、そのままメモ帳で見るとなかなか見にくいです。そこで、ファイルをexcelで開きましょう。その時ポイントとなるのが、値と値の間に「，」などの印をつけることです。excelでは指定した文字で文字列を分解する機能があります。そうすれば、とても簡単にデータが見れますし、グラフを作ることも容易です。
![スクリーンショット 2018-10-25 20.15.17.png](https://qiita-image-store.s3.amazonaws.com/0/183170/a68cb287-fea1-7551-1f45-0e4a11d22b08.png)
![スクリーンショット 2018-10-25 20.16.55.png](https://qiita-image-store.s3.amazonaws.com/0/183170/3fc82cf3-777b-7c1b-33fc-7a7ed221aaef.png)

#最後に#
今回はファイルの扱い方について紹介しました。printf系統は非常によく使うので、覚えておいた方が良いでしょう。
さて、次回はいよいよタスク・周期ハンドラに入っていきたいと思います。僕は理解するのに1年かかりました…

次回:[第8回](https://qiita.com/koushiro/items/22a10c7dd451291fd95b)
前回:[第6回](https://qiita.com/koushiro/items/bd081f0613077addb88d)