通信工学概論(H22年度)
演習資料1
C
C
言語におけるファイル入出力
言語におけるファイル入出力
C言語の復習
コンパイルして実行しましょう
(
コンパイルオプションを忘れずに!
)
..[4]%
gcc –Wall –o hoge hoge.c
↵
..[5]%
./hoge
↵
1番目 :
1
↵
2番目 :
2
↵
3番目 :
3
↵
4番目 :
0
↵
6
..[6]%
便利なコマンド
..[4]% gcc –Wall –o hoge hoge.c
↵
..[5]%
!g↵
gcc –Wall –o hoge hoge.c
..[6]%
!! ↵
gcc –Wall –o hoge hoge.c
..[7]%
!.
./hoge
1番目 :
リダイレクション ( ! ) を使おう
!g は,一番最近の,
gからはじまるコマンド
を再実行
!!は,直線のコマンド
を再実行
!.は,一番最近の
.から始まるコマンド
を再実行
!ls や !gcc などの指定
も可能
よく使うコマンド
cp : ファイルのコピー
cp 元のファイル名 コピー後のファイル名
例. cp hoge.c hoge2.c
hoge.cを,同じ場所にhoge2.cと言う名前でコピー
cp hoge.c jikken1/hoge3.c
hoge.cを,jikken1フォルダの下にhoge3.cと言う名前でコピー
mv : ファイルの移動 ( 名前の付け替えにも使える )
mv 移動したいファイル名 移動先とファイル名
例. mv hoge.c jikken1/hoge2.c
hoge.cを,jikken1フォルダの下にhoge2.cと言う名前で移動
hoge.cは無くなる
cat : ファイルの中身をコンソールに表示
cat ファイル名
または
cat ファイル名 | more
例. cat hoge.c | more
hoge.cが長い場合,コンソールの下限まで表示したら一旦停止.
Return (Enter) キーを押すと次の1ページ分を表示.途中で終了
したい場合は q で終了.
ファイルとは
これまでの入力は
標準入力装置
( キーボード ),
出力は
標準出力装置
( ディスプレイ )でした
これらを
ファイル
で行うことも可能です
ファイルは計算機内に保存された
情報の集合です
プログラムソース,データ,
画像などの様々なファイル
形式があります
この実験では,C言語を使って
ファイルの読み書きができるよう
になることを目指します
計算機間でのやり取り メディアでの持出し ファイルとして計算機に 保存されているとファイルとは
秘
C言語で扱うファイルは,本やノートと
同等だと考えられます
本かノートが一冊あるとします
Q1:読むにはどうすればいいですか?
書き込むにはどうすればいいですか?
A1:本やノートを開く必要があります
→ fopen関数
Q2:開けば何でも読めますか?
A2:あなたの読めない言葉もありますよね?
→ テキスト,バイナリ
Q3:開けば何にでも書けますか?
A3:読むだけの場合(本),書き込む場合(ノート),
読み書きする場合が考えられます
→ オープンモード
Q4:読み書きが終わりました
A4:ノートを閉じましょう
→ fclose関数
ノート( ファイル )があっても 開かないと読み書き できませんよね秘
終わったら閉じないと 誰かに読まれたり 消されたりするかも・・・C言語でファイルを扱う
サンプルプログラムで
説明します
/* ファイルから読み込んだ整数値を加算 */ #include <stdio.h>
#include <stdlib.h> /* exitを使うために定義する必要がある */ int main(void)
{
int i, dat, sum = 0; FILE *fp;
if ( ( fp = fopen( “indata.dat”, “r” ) ) == NULL ) /* 入力ファイルのオープン */ { printf( “ファイルが見つかりません¥n” ); exit(1); } for (i = 1 ; ; i++){ fscanf(fp, “%d”, &dat); /* ファイルからデータをひとづつ読み込む */ if(dat == 0) /* 0データが来たらループから抜ける */ break; sum += dat; /* データの値を加算する */ } printf(“%d¥n”, sum); /* 結果の表示 */ fclose(fp); /* 入力ファイルのクローズ*/ return(0); }
お決まりの準備部分
お決まりの後処理部分
ファイルオープン
FILE *fp;
if ( ( fp = fopen( “indata.dat”, “r” ) ) == NULL ); /* 入力ファイルのオープン */ { printf( “ファイルが見つかりません¥n” ); exit(1); }
お決まりの準備部分
FILE *fp;
の意味
・
C言語でファイルを扱う場合,プログラム上で使用する
ファイルにファイルポインタを割り当てる必要がある。
・ ファイルを作成・オープンする → FILE構造体が作られる
・ FILE構造体についてユーザが意識する必要はない
・ FILE構造体はstdio.hで定義されているため,プログラム
には
必ずstdio.hをインクルードしなければならない
(高水準入出力関数)
・
複数のファイルを同時に扱う場合,各々のファイルについ
てファイルポインタを割り当てる
ファイルポインタの宣言
b
秘
a
FILE *fp; の意味
c
/* fpというファイルポインタを宣言 */
FILE *fp;
d
e
「いまからファイルを使うぞぉー! 」
と宣言しているに過ぎないの
で,どのファイルとも関係があ
りません。
ファイルオープンの方法
FILE *fp;
if ( ( fp = fopen( “indata.dat”, “r” ) ) == NULL ); /* 入力ファイルのオープン */ { printf( “ファイルが見つかりません¥n” ); exit(1); }
お決まりの準備部分
fp = fopen( ファイル名, モード )
・
第一引数はファイル名
で,“ ”でくくることで直接指定できる。
当然,変数として扱うことも可能
・
第二引数はモード
で,ここで指定した状態でファイルが開く
ファイルオープンのモード
既存ファイルの最後に追加 新しいファイルが作成される バイナリ追加書込 モード “ab” 内容が失われる 新しいファイルが作成される バイナリ書込モード ”wb” 読込できる状態で開く エラー バイナリ読込モード ”rb” 既存ファイルの最後に追加 新しいファイルが作成される 追加書込モード “a” 読込できる状態で開く エラー 読込モード ”r” 内容が失われる 新しいファイルが作成される 書込モード ”w” ファイルが存在する場合 ファイルが存在しない場合 動作 第二引数( モード )・ bは読み書きするファイルの形式がバイナリであることを指定している
・ UNIXではファイル形式の区別はないが,明示的に指定したほうが良い
( Windowsなどでは必須 )
・ テキストファイルは人が読める状態で記述・保存されたファイル
(例. C言語のプログラムソース,htmlファイル )
・ バイナリファイルは計算機が理解しやすい状態で記述・保存されたファイル
( 例. 画像,C言語の実行ファイル)
ファイルオープンの実際
e
秘
a
b
d
c
/* ファイルbを読み込みモードでオープン */
fp = fopen( “b”, “r” ) ;
b
ファイルbを指定して
fp = fopen( ファイル名, モード ) ; の意味
fp
読めるように開く
このときファイルポインタは
1a b c d e f g h i j k l m n o p q r s
t u v w x y z k o n o m o j i r e t s
u n o n a r a b i n i h a i m i h a a
r i m a s e n ・ ・ ・
fpが示しているのは
ノートの最初のページ
の最初の文字の位置
です。
実際には,ファイルの
中身は一次元の情報
の集合です。
ここでは,直感的に理解
しやすいように2次元の
ノートを例にしています。
ファイルクローズ
fclose(fp); /* 入力ファイルのクローズ*/お決まりの後処理部分
fclose (fp);
の意味
・ オープンしたファイルをクローズする
・ プログラム終了後( ファイルが不要になったとき )に
必ず
ファイルをクローズしなければならない
・ 複数のファイルを同時に扱っている場合,各々のファイル
をすべてクローズしなければならない
ファイルクローズ
e
秘
a
b
d
c
b
元に戻す
fp
fclose( fp ) ; の意味
/* ファイルbをクローズ
ファイルポインタfpの設定解除 */
fclose(fp) ;
・ ファイルポインタの割り当ては
開放される
・ 当然,ファイルとファイルポイン
タの関係は解消される
終わったら
閉じて
複数のファイルを扱う
e
秘
a
b
d
c
b
d
fp1
fp2
FILE *fp1;
FILE *fp2;
・
・
・
fp1 = fopen( “b”, “r” );
fp2 = fopen( “d”, “w” );
・
・
・
fclose( fp2 );
fclose( fp1 );
ファイルを 使うぞ! 別のファイル も使うぞ! ファイルbを 読むぞ! ファイルdに 書き込むぞ! 終わったか ら仕舞おう 終わったか ら仕舞おうファイルを扱うために
FILE *fp
if ( ( fp = fopen( ファイル名, モード ) ) == NULL );
/* ファイルオープン */
{
printf( “ファイルが見つかりません¥n” );
exit(1);
}
fclose (fp);
/* ファイルクローズ */
・
・
・
処理
この使い方を覚えてしまいましょう!
ファイル入出力
ファイルのオープンに成功すると,ファイルポインタを用いて
ファイルからデータを入力したり,ファイルへ出力することが
可能となります。
入出力関数の例として
fgetc / fputc : 一文字単位のファイル入出力
fgets / fputs : 一行単位のファイル入出力
fscanf / fprintf : 書式付のファイル入出力
fread / fwrite : ブロック単位のファイル入出力
などがあります。
一文字単位のファイル入出力
fgetc / fputc 関数
・ getchar関数,putchar関数のファイルポインタ版
c = fgetc(fp);
・ 引数に指定したファイルポインタの初期の読み込み位置
から一文字( 8ビット )読み込み,読み込み位置を一文字
移動させる
・ 読み込み位置がファイルの一番最後に来るとEOF
( End of File ) を返す
・ cは
必ずint型
の変数としなければならない
fputc(c, fp)
・ 第一引数は出力する文字
・ 第二引数は書き込むファイルのファイルポインタ
fgetc/fputcの実際
a b c d e f g h i j k l m n o p q r s
a
オープンしたファイル
変数 C
1回目の読み込み
b
2回目の読み込み
c
3回目の読み込み
何が入るでしょう?
4回目の読み込み
EOFを示す数値が入る
( ユーザは意識しなくても良い )
ファイルの最後
・ 読み込み位置を次に移動しながら,一文字ずつ読み込んでいく
・ まずunsigned char型として読み込む
・ int型に変換して呼び出し側に返す
・ 実際にfgetcが返すのはファイルから読み込んだ文字となる
一文字単位のファイル入出力
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fpr; FILE *fpw; int c; /* 入力ファイルのオープン */if ( ( fpr = fopen( “rsample.txt”, “r” ) ) == NULL ) {
printf( “入力ファイルが見つかりません¥n” ); exit(1);
}
/* 出力ファイルのオープン */
if ( ( fpw = fopen( “wsample.txt”, “w” ) ) == NULL ) {
printf( “出力ファイルが開けません¥n” ); exit(1);
}
/* データ処理 */
while (( c = fgetc(fpr) ) != EOF) { fputc( c, fpw ); } /* 出力ファイルのクローズ */ fclose(fpw); /* 入力ファイルのクローズ */ fclose(fpr); return(0); }
・ サンプルプログラムを入力・実行し,処理の内容を確認しましょう
・ はじめに,muleで 「複数行にわたる任意のアルファベット群 」のファイルを
作成しましょう。ファイル名は 「 rsample .txt 」 とします
・ 実行後,cat コマンドで書き込んだファイルの中身を確認しましょう
一行単位のファイル入出力
fgets / fputs 関数
・ gets関数,puts関数のファイルポインタ版
fgets( buf, バッファサイズ ,fp);
・ 引数に指定したファイルポインタの初期位置から一行
読み込み,読み込み位置を次の行の先頭に移動させる
・ 読み込み位置がファイルの一番最後に来るか読み込み
エラーが発生すると NULL を返す
・ bufは一般に
char型の配列
fputs(buf, fp)
・ 第一引数は出力する文字
・ 第二引数は書き込むファイル のファイルポインタ
fgets/fputsの実際
a b c d e
オープンしたファイル
f g h i j k l m n
o p q r s t
a
b
c
d
e ¥n
1回目の読み込み
f
g
h
i
j
k
l
m n
¥n
¥0
¥0
buf
文字列では,null文字 以降は無視される 改行文字かファイルの終了 がくるまで一行読み込みの
変
化
文字列の最後に null文字を追加2回目の読み込み
3回目の読み込み
o
p
q
r
s
t
¥0
・ 読み込み位置を次の行の先頭に移動しながら,一行ずつ読み込んでいく
・ 読み込んだ文字列の最後にnull文字を追加する ( 改行コードを読み込んだ場合も同様 )
・ 出力結果には改行コードが含まれている
・ 結果として,文字列を入出力する関数である
一行単位のファイル入出力
#include <stdio.h> #include <stdlib.h> #define MAX_STRINGS 50 int main(void) { FILE *fpr; FILE *fpw; int c; char data[MAX_STRINGS]; /* 入力ファイルのオープン */if ( ( fpr = fopen( “rsample.txt”, “r” ) ) == NULL ) {
printf( “入力ファイルが見つかりません¥n” ); exit(1);
}
/* 出力ファイルのオープン */
if ( ( fpw = fopen( “wsample.txt”, “w” ) ) == NULL ) {
printf( “出力ファイルが開けません¥n” ); exit(1);
}
/* データ処理 */
while ( fgets( data, MAX_STRINGS, fpr ) != NULL) { fputs( data, fpw ); } /* 出力ファイルのクローズ */ fclose(fpw); /* 入力ファイルのクローズ */ fclose(fpr); return(0); }
・ さきほどのサンプルプログラムを変更してみましょう
・ 配列の中身がどのようになるかわかりますか?
・ このサンプルプログラムのエラーを見つけましょう
書式付のファイル入出力
fscanf / fprintf 関数
・ scanf関数,printf関数のファイルポインタ版
fscanf (fp, 書式文字列, 書式に埋め込む値);
fprintf (fp, 書式文字列, 書式に埋め込む値);
・ ファイルポインタを第一引数に指定
・ その他は scanf / printf と同じ
・ ファイルから読み込んだ内容がはいる変数は,
当然ながら
指定した書式と同じ
にする必要が
ある
バイナリ形式
バイナリー(Binary)とは、「0」と「1」の2種類の
数字で表現する2進数の意味
2進数で表されたデータ (バイナリー・データ)や
実行形式のプログラム(バイナリー・コード)を
バイナリー形式と言う
計算機で扱うデータは、テキスト形式でなければ
バイナリー形式である
バイナリ形式の長所 ・ 短所
長所:
- 保存するデータに制約がない
- 多彩な表現の情報を保存・交換することが可能
( 任意のヘッダ情報を付加することができる )
短所:
- ファイルフォーマットが分からないと内容を読む
ことができない
- 互換性に劣る( テキスト形式に比較して )
バイナリデータとは ( 8 bitの例 )
00
08
54
04
09
49
3C
10
38
46
2F
21
3F
0B
01
39
19
0E
41
60
56
3B
59
57
FF
0
8
84
4
9
73
60
16
56
70
47
33
63
11
1
57
25
14
65
96
86
59
89
87
255
00000000
00001000
01010100
00000100
00001001
01001001
00111100
00010000
00111000
01000110
00101111
00100001
00111111
00001011
00000001
00111001
00011001
00001110
01000001
01100000
01010110
00111011
01011001
01010111
11111111
10進数 : 人が理解しやすい
16進数 : 計算機が考えやすい
2進数 : 実際のメモリ内容
8 bit = 1 byte
2進数を示す箱が
8個(8桁分)ある
0 ~ 255を表現
できる
バイナリデータとは ( 8 bitの例 )
73 60
4
9
3
C
0
1
0
0
1
0
0
1
0
0
1
1
1
1
0
0
10進数
16進数
2進数
0x49 = 4×16
1
+9×16
0
= 73
01001001 = 0×2
7
+ 1×2
6
+ 0×2
5
+ 0×2
4
+ 1×2
3
+ 0×2
2
+ 0×2
1
+ 1×2
0
= 73
= 0x ( 0×2
3
+ 1×2
2
+ 0×2
1
+ 0×2
0
),
0x ( 1×2
3
+ 0×2
2
+ 0×2
1
+ 1×2
0
)
= 0x 49
下位のデータ
上位のデータ
最下位のデータ
最上位のデータ
fread/ fwrite関数
fread( buf, サイズ, カウント, fp);
・引数に指定したファイルポインタの初期位置から
サイズ( byte単位 )毎にカウント( 最大値 )まで
データを読み込む
・実際に読み込まれたデータの数を返す
例.
size = fread( buf, sizeof(long), 100, fp);
※ intやlongなど,計算機環境によってサイズ( byte数 )が
異なることがあるため,sizeof演算子でデータ型の大きさ
(バイト単位)を計算するのがよい
fwrite( buf, サイズ, カウント, fp);
・引数に指定したファイルポインタの初期位置から
サイズ毎にカウントまでデータを書き込む
課題: BMPファイルの入出力
24 bit ( 1677万色 )のWindows Bitmap形式で保存
された画像ファイル
bmpsample.bmp
を読み込み,
Red,Blue,Greenのいづれかの色情報のみを残し
て他の色情報を排除した画像を作成しなさい
bmpsample.bmpの画像サイズ( 縦×横の大きさ )
はヘッダ情報を読み込むことで認識しなさい
作成するファイルは縦 ・ 横の画素数および色数
が元画像と同じままのWindows Bitmap形式とする
元の画像
Redだけ
Blue+Green
Green
だけ2倍
参考資料
BMPファイル全体の構造
BMP フォーマットはヘッダの種類によって Windows Bitmap と OS/2 Bitmap
の2種類に分けられる。これらの2つのフォーマットは,ヘッダのサイズが
異なっている。
BMP ファイル全体 の構造
ファイルヘッダ
BITMAPFILEHEADER (14byte)
情報ヘッダ
BITMAPINFOHEADER (Windows)
または
BITMAPCOREHEADER (OS/2)
+
カラーパレット(1, 4, 8 bit の場合必要)
画像データ
※ カラーパレーットがない場合、各画素に割り振られた値は
そのまま色(または明るさ)を示す。
カラーパレットがある場合は、各画素の値はパレットを参照
するための値であり、色(または明るさ)はパレットで定義
されたものとなる。
ファイルヘッダ
[BITMAPFILEHEADER]
最初に
14 byte 固定
のファイルヘッダ部分がある。
最初の 2 byte が
“BM”
かどうかで、ビットマップファイルが識別される。
BITMAPFILEHEADER
bfType
2
byte
ファイルタイプ
'BM' - OS/2, Windows
Bitmap
bfSize
4
byte
ファイルサイズ (byte)
bfReserved1
2
byte
予約領域
常に 0
bfReserved2
2
byte
予約領域
常に 0
bfOffBits
4
byte
ファイル先頭から画像データま
でのオフセット (byte)
情報ヘッダ < Windows >
[
BITMAPINFOHEADER
]
BITMAPINFOHEADER (1)
biSize 4 byte 情報ヘッダのサイズ (byte)
40 byte
biWidth 4 byte 画像の幅 (ピクセル) biHeight 4 byte 画像の高さ (ピクセル) biHeight の値が正数なら,画像データは 下から上へ biHeight の値が負数なら,画像データは 上から下へ biPlanes 2 byte プレーン数 常に 1biBitCount 2 byte 1 画素あたりのデータサイズ (bit)
1 - 2 色ビットマップ 4 - 16 色ビットマップ 8 - 256 色ビットマップ 16 - 65536色(high color)ビットマップ 24 - 1677万色(true color)ビットマップ 32 - 1677万色(true color)ビットマップ biCopmression 4 byte 圧縮形式 0 - BI_RGB (無圧縮)
1 - BI_RLE8 (RunLength 8 bits/pixel) 2 - BI_RLE4 (RunLength 4 bits/pixel) 3 – Bitfields
情報ヘッダ < Windows >
[
BITMAPINFOHEADER
]
BITMAPINFOHEADER (2)
biSizeImage 4 byte 画像データ部のサイズ (byte) 96dpi ならば3780 0 の場合もある biXPixPerMeter 4 byte 横方向解像度 (1mあたりの画素 数) 96dpi ならば3780 0 の場合もある biYPixPerMeter 4 byte 縦方向解像度 (1mあたりの画素 数) 96dpi ならば3780 0 の場合もある biClrUsed 4 byte 格納されているパレット数 (使用 色数) 0 の場合もある biCirImportant 4 byte 重要なパレットのインデックス 0 の場合もある
情報ヘッダ < OS/2 >
[
BITMAPCOREHEADER ]
BITMAPCOREHEADER
biSize 4 byte 情報ヘッダのサイズ (byte)
12 byte
bcWidth 2 byte 画像の幅 (ピクセル) bcHeight 2 byte 画像の高さ (ピクセル) bcHeight の値が正数なら,画像データは 下から上へ bcHeight の値が負数なら,画像データは 上から下へ bcPlanes 2 byte プレーン数 常に 1bcBitCount 2 byte 1画素あたりのデータサイズ (bit)
1 - 2 色ビットマップ 4 - 16 色ビットマップ 8 - 256 色ビットマップ 16 - 65536色(high color) 24 - 1677万色(true color)ビットマップ 32 - 1677万色(true color)ビットマップ
Windows BMP 24bit の構成
42 4D
・
・
・
BITMAPFILEHEADER ( 14 byte )
BITMAPINFOHEADER ( 40 byte )
Data ( 任意のサイズ )
0x42 が ‘B’ をあらわす
0x4D は?
は1 byte を示しているおり,16進数で表記している。実際は
8個のメモリに2進数で保存されている
実際には
Windows BMP 24bit のデータ構造
(31,0)
(31,31)
(0,0)
(0,31)
32画素×32画素のWindows BMP
画像データは基本的に左下から
右上へ向かって並んでいるので、
モニタに表示されるものとは並び順
が異なる
( BITMAPINFOHEADER の biHeight で決まる )