PowerPoint

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