180 likes | 254 Views
10: ファイル入出力. Linux にログインし、以下の講義ページを開いておく こと http://www-it.sci.waseda.ac.jp/teachers/w483692/CPR1 /. 今日の内容. 標準ライブラリ 関数により ファイルの出力 を行う 画像 ファイルの生成を例題として 配列の作成を復習する 関数を作ってプログラムを構造化する. PGM 画像ファイル. Portable gray map image 他 に、 PPM ( カラー ), PBM (2 値 ) がある 単純なフォーマット テキスト形式とバイナリ形式がある.
E N D
C プログラミング入門 基幹2 (月4) 10: ファイル入出力 Linux にログインし、以下の講義ページを開いておくこと http://www-it.sci.waseda.ac.jp/teachers/w483692/CPR1/
今日の内容 • 標準ライブラリ関数によりファイルの出力を行う • 画像ファイルの生成を例題として • 配列の作成を復習する • 関数を作ってプログラムを構造化する C プログラミング入門 基幹2 (月4)
PGM 画像ファイル • Portable gray map image • 他に、PPM (カラー), PBM (2値) がある • 単純なフォーマット • テキスト形式とバイナリ形式がある PGM 画像ファイルの例 (テキスト形式) PGM 画像 (4×4 pixel) フォーマットを識別するマジックナンバー P2 4 4 255 0 127 255 255 127 255 127 0 127 255 127 0 0 127 255 255 header 横幅 高さ [pixels] 画素値の最大値 (=白) 画素値 C プログラミング入門 基幹2 (月4)
実演: PGM 画像ファイルを手で作る • テキストエディタで、前頁のテキストファイルを作り、 mini.pgmというファイル名で保存する • ダブルクリックして画像ビューアで開く • 4x4 ピクセルなので、拡大しないと見えない • 手で作ると大変なので、プログラムで作りだそうというのが、今日の本題です。 C プログラミング入門 基幹2 (月4)
ファイル入出力の流れ • ファイルを開く • プログラムから指定してた名前のファイルに読み書きできるように準備すること • ファイルを読む・ファイルに書き込む • I/O (input/output) と呼ばれる • 通常は、ファイルの内容を順番にアクセスする (シーケンシャルアクセス; sequential access) • ⇄ランダムアクセス (random access) • ファイルを閉じる • ファイルの処理を完了し、バッファに残っているものを全て出力する C プログラミング入門 基幹2 (月4)
例題: PGM ファイルの出力 fopen(), fprintf(), fclose()などは stdio.hで宣言されている #include <stdio.h> int main(void) { FILE *fp; fp = fopen("mini.pgm", "w"); fprintf(fp, "P2\n"); fprintf(fp, "4 4\n"); fprintf(fp, "255\n"); fprintf(fp, " 0 127 255 255\n"); ... fclose(fp); ... 開いたファイルを識別するため情報を指すポインタ ファイルポインタと呼ばれる。変数名は何でもよい ファイルを mini.pgmという名前で、書きこみモードで開く ファイルに文字列を書きこむ ファイル閉じる C プログラミング入門 基幹2 (月4)
ファイルを開く • fopen() にファイル名とモードを指定 • FILE 型へのポインタが返る • ファイルを操作するために必要な情報が入っている • 変数に入れて使う fopen() のプロトタイプ 構造体(今後の講義で解説)であるが、 内容を理解する必要は通常ない #include <stdio.h> FILE *fopen(const char *filename, const char *mode ); C プログラミング入門 基幹2 (月4)
ファイルオープンの失敗 • ファイルが開けない場合がある • 読み込みモードで、ファイルが存在しない場合 • 書き込みモードで、書き込み禁止の場所の場合 • etc... • 失敗した場合 NULL ポインタが返る { FILE *fp; fp = fopen("some.file", "w"); if(fp == NULL){ // エラー処理を行う C プログラミング入門 基幹2 (月4)
ファイルに文字列を書きだす • ファイルに書き込むための関数を利用 • fprintf()は printf()と同じ使い方だが、第1引数にファイルポインタを与える fprintf() のプロトタイプ #include <stdio.h> intfprintf(FILE *stream, const char *format, ...); その他のファイル書き込み関数 C プログラミング入門 基幹2 (月4)
ファイルを閉じる • ファイルポインタを引数に fclose()を呼ぶ • ファイルの書き込みは、効率のためにバッファリングがされるが、 fclose() は自動的に fflush() を呼び出す fclose() のプロトタイプ #include <stdio.h> intfclose(FILE *fp); C プログラミング入門 基幹2 (月4)
大きい画像を作る • 特定のパターンならループを使ってファイルを書きだせば書けそう • 複雑な場合は? 画素値 0, 50, 100, 150 のグラデーション { FILE *fp; inti; fp = fopen("grad.pgm", "w"); ... for(i = 0; i < 4; ++i) { fprintf(fp, "%d %d %d %d\n", i*50, i*50, i*50, i*50); } C プログラミング入門 基幹2 (月4)
配列をキャンバスに使う • 配列の各要素を画素値だと思って、そこに絵を書く • ループを使ってファイルに書き出す unsigned char image[32][32] 画素値の配列を作って、 絵を書く 0 0 255 0 0 0 0 255 0 0 0 0 0 0 0 0 0 21 0 0 C プログラミング入門 基幹2 (月4)
コード例 { FILE *fp;int x, y; unsigned char image[32][32] = {{0}}; // image に絵を書く... // ファイル出力 fp = fopen("image.pgm", "w"); // エラーチェックをしてヘッダを書きだす ... for(y = 0: y < 32; ++y) for(x = 0; x < 32; ++x) { fprintf(fp, "%d ", image[y][x]); } fclose(fp); ... 32×32ピクセルの配列をつくり、ゼロで初期化する。 配列 image に画素値をいろいろ入れる 空白でそれぞれの画素値が区切られるようにする 行、列の順番で考えている C プログラミング入門 基幹2 (月4)
PGM のバイナリ形式の場合 • 1 バイト (unsigned char) の配列をそのままファイルに書いた形式。ヘッダは P5 • fwrite() を使って書きだす unsigned char image[32][32] 0 0 255 0 0 0 0 255 0 0 fwrite() のプロトタイプ 0 0 0 0 0 #include <stdio.h> size_tfwrite(constvoid *buf, size_tsize, size_tcount, FILE *fp); 0 0 21 0 0 bufの指す場所から、 size [bytes] * count [個]の領域の値を fpの指すファイルに書き出す C プログラミング入門 基幹2 (月4)
配列に絵を書く関数 • 配列に様々な絵を書きこむ関数を実現するには、配列へのポインタとサイズを渡す • 配列のサイズを関数は直接知ることができない // width x height の画像 imgを color で塗りつぶす void fill(unsigned char *img, int width, int height, int color) { int x, y; for(y = 0; y < height; ++y) { for(x = 0; x < width; ++x) { // NG:img[y][x] = color; img[x + y*width] = color; } } } width x height の領域の先頭へのポインタ ポインタ引数で配列の先頭位置を知っただけなので、2次元配列としてアクセスできない。代わりに、座標からメモリ上の位置を計算する C プログラミング入門 基幹2 (月4)
配列に文字列を書きこむ • ファイル名などをプログラムで生成 • 自動的に日付を付ける • 連番にする • etc... • printf()は標準出力 (画面) に文字列を出す • fprintf()はファイルに文字列を出す • 文字配列に出力するには、 sprintf()を使う sprintf() のプロトタイプ メモリ領域の先頭へのポインタ #include <stdio.h> intsprintf(char *buffer, const char *format, ...); C プログラミング入門 基幹2 (月4)
sprintf() の使い方 • sprintf()が書きだすのに十分大きい配列を用意しておく • はみ出してもエラーは出ない • 他の使われている領域を破壊する可能性がある 実用上は 256 など、大きい値を指定する { char filename[10] = ""; sprintf(filename, "img-%d.pgm", 5); たとえば、ここが変数となり、ループなどで変化する char filename[10] この例ではぎりぎりの文字数となる。 5 の代わりに 10 としたら、はみ出してメモリ破壊をしてしまう。 '\0' '\0' '\0' '\0' '\0' '\0' '\0' '\0' '\0' '\0' '-' 'm' 'g' 'g' 'm' 'p' 'i' '5' '.' '\0' C プログラミング入門 基幹2 (月4)
テキストファイルの読み込み • 手順 • fopen() のモードを "r" で開く • fgetc(), fgets() で文字列を読み込み、メモリに書きこむ • fscanf() によって文字列を数値などに変換してメモリに書きこむ • その後、 sscanf() によって処理をしてもよい • 読み込みの処理は、書きこむよりも難しいことが多い • ファイルに「正しい」情報があるとは限らない C プログラミング入門 基幹2 (月4)