ce-6. ファイル,配列
1
金子邦彦
C プログラミング応用)(全14回)
URL: https://www.kkaneko.jp/pro/c/index.html
C言語におけるファイル入出力の基本操作と、配列を用
いたデータ処理の実装方法の習得。
学習内容の構成
1. ファイル処理の基本fopen/fcloseによるファイル
のオープン・クローズ、fgetsによる行単位の読み込
2. テキストファイル操作名簿データの読み込みと
sscanfによるデータ抽出・表示
3. 一次元配列0から始まる添字によるデータ管理と、
ベクトル内積計算への応用
4. 多重ループ:ループの入れ子構造による棒グラフ描
前提:C言語の基本文法(変数、for文、printf
意義:実用的なデータ処理プログラムの作成能力
2
ファイル処理
3
ファイル読み込み
4
ファイル
プログラム
ファイルの中身は変わらない
ファイル書き出し
5
ファイル
プログラム
ファイルの中身が変わる
•ファイルは伸び縮みすることが
ある
例題1.テキストファイル形式
ファイルからのデータ読み込み
次のような名簿ファイル(テキストファイル形
式)を読み込んで,1列目の氏名と,3列目の住
所だけを表示するプログラムを作る
各データは,半角の空白文字(1つまたは複数)で区
切られる
金子邦彦 1200/01/01 福岡市東区箱崎3丁目 392-123-8234
○○×× 1300/12/31 福岡市東区貝塚団地 492-252-7188
●●■■ 0800/05/31 福岡市東区香椎浜1丁目 592-824-7144
6
3行のテキストファイル
テキストファイル形式
行単位での読み書きに意味がある。
人間が「目で見て」読むことができるファイル。
7
MPL 40
pattern1 = 786
pattern2 = 1
pattern3 = 979
pattern4 = 0
テキストファイルの例
<FF>0<FF><E0>^@
^PJFIF^@^A^B^A^
@<96>^@<96>^@
^@<FF><E0>~JFXX
^@^P<FF>00<FF>U
^@^C^@^
テキストファイルでない
バイナリファイルの例
画像のファイル
8
#include "stdio.h"
#include <math.h>
#pragma warning(disable:4996)
int main()
{
char line[100];
char name[100];
char birth[100];
char address[100];
FILE *in_file;
int ch;
in_file = fopen("d:¥¥Book1.txt", "r");
if ( in_file == NULL ) {
return 0;
}
while( fgets( line, 100, in_file ) != NULL ) {
sscanf_s( line, "%s %s %s", name, birth, address );
printf( "name=%s, address=%s¥n", name, address );
}
fclose(in_file);
ch = getchar();
ch = getchar();
return 0;
}
例題1の手順
1.準備
演習用のデータファイル d:¥Book1.txt を各自で作成
(本資料のページ9,10,11,12,13)
2.ビルドと実行
例題1のプログラムを各自で実行し,実行結果を確認
9
各自で行ってください
(実行結果の確認まで)
10
まず,データファイル
d:¥Book1.txt を準備する
(テキストファイル形式)
金子邦彦 1200/01/01 福岡市東区箱崎3丁目 392-123-8234
○○×× 1300/12/31 福岡市東区貝塚団地 492-252-7188
●●■■ 0800/05/31 福岡市東区香椎浜1丁目 592-824-7144
d:¥Book1.txt の例
(全角の空白文字が混ざっていると
動かないことがあるので注意してください)
半角の
空白文字
11
「メモ帳」を起動している
12
コピーして ・・・
13
貼り付ける
14
「ファイル」
「名前を付けて保存」
保存する場所
は,Zドライ
ファイル名は
Book1.txt
15
ビルド後の画面
ビルドが正常終了したこと
を示すメッセージ
ビルドの手順:
「ビルド」→「○○のビル
ド」
「1.正常終了」を
確認
16
実行中の画面
実行の手順:
「デバッグ」→
「実行」
実行ウインドウが現れる
#include "stdio.h"
#include <math.h>
#pragma warning(disable:4996)
int main()
{
char line[100];
char name[100];
char birth[100];
char address[100];
FILE *in_file;
int ch;
in_file = fopen("d:¥¥Book1.txt", "r");
if ( in_file == NULL ) {
return 0;
}
while( fgets( line, 100, in_file ) != NULL ) {
sscanf_s( line, "%s %s %s", name, birth, address );
printf( "name=%s, address=%s¥n", name, address );
}
fclose(in_file);
ch = getchar();
ch = getchar();
return 0;
}
17
while による繰り返し
ファイルオープンに失敗した
ときのみ実行される部分
fopen 関数で,NULL
ファイルオープンの失敗
fgets 関数で,NULL
ファイルの終わり
ファイル操作
ファイルのオープンとクローズ
fopen ファイルの読み書きを行う前に、ファイ
ルはオープンされねばならない
fclose ファイルの読み書きが終わったら、ファ
イルはクローズされねばならない
ファイルの読み込み
fgets 1行単位の読み込み
fread バイト単位での読み込み(1バイト,複
数バイト)
ファイルの書き出し
fputs 1行単位での書き出し
fwrite バイト単位での書き出し(1バイト,複数バイ
ト)
fprintf 整形しての書き出し
18
オープンモード
“rモード
読み込みモード
引数fileで指定したファイルが存在しないか,読み込み
不可能な場合には,オープンすることができない.
“wモード
書き出しモード
引数fileで指定したファイルが存在しない場合には,
ファイルが新たに作成される.ファイルがすでに存在
した場合,ファイル中のデータはすべて捨てられる
(ファイルの長さは0になる).
19
in_file = fopen("d:¥¥Book1.txt", "r");
ファイル名
(文字列)
オープンモード
(文字列)
例題1のプログラムが
行っていること
20
プログラムが使う
メモリ空間
line
name
birth
address
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
例題1のプログラムが
行っていること
21
データファイル
プログラムが使う
メモリ空間
fgets
読み出し
(1行分)
line
name
birth
address
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
金子邦彦 1200/01/01 福岡市東区箱崎3丁目 392-123-8234
○○×× 1300/12/31 福岡市東区貝塚団地 492-252-7188
●●■■ 0800/05/31 福岡市東区香椎浜1丁目 592-824-7144
金子邦彦 ・・・
例題1のプログラムが
行っていること
22
データファイル
プログラムが使う
メモリ空間
fgets
読み出し
(1行分)
line
name
birth
address
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
100バイトの
メモリエリア
金子邦彦 1200/01/01 福岡市東区箱崎3丁目 392-123-8234
○○×× 1300/12/31 福岡市東区貝塚団地 492-252-7188
●●■■ 0800/05/31 福岡市東区香椎浜1丁目 592-824-7144
金子邦彦 ・・・
金子邦彦
1200/01/01
福岡市東区箱崎3丁目
2行目以降も
同様の処理が続く
sscanf での
処理
23
実際のメモリの中身
メモリの中身を画面表示したもの
24
実際のメモリの中身
メモリの中身を画面表示したもの
ここでは,16バイトごとに
区切って,1行で表示
25
実際のメモリの中身
メモリの中身を画面表示したもの
1バイト
16進数2桁 00からFF
2進数では8桁 00000000 から 11111111
10進数では 0 から 255 までの256通り
「バイト」は,データの基本
単位
26
実際のメモリの中身
address
100バイト)
birth
100バイト)
name
100バイト)
line
100バイト)
プログラムとデータ
27
プログラムが使うメモリ空間
fgets( line, 100, in_file )
ファイルの
読み込み
(1行単位)
line[0]
line[1]
line[99]
sscanf_s( line, "%s %s %s", name, birth, address );
1行分の表示
データの取り出し
namebirthaddress
printf( "name=%s, ...
fgetsの振る舞い
ファイルの1行読み込み
ファイルの一行分を読み込んで、末端の¥0を付ける
ファイルには、各行の終わりに、改行文字(¥n)が付いて
いる(目には見えない)
読み込み先(文字の配列)のサイズが、ファイルの1行
の長さより長いときは、「残りの部分」は変化しない
28
文字の配列
line
ファイル
M a r k
改行文字
文字列の末端
変化しない
Mark¥n
1行読み込むと・・・
16進数
の「0A
16進数
の「00
fgets での「100
29
文字の配列
ファイル
文字列の末端
fgets( line, 100, in_file )
配列のサイズが100ならば、読み込めるデータの
本体(「改行文字」を除く)は,最大で98文字まで
100バイトに達したら
行末になっていなくても読み込み終了せよ
16進数
の「00
16進数
の「0A
改行文字
配列
30
一次元配列
配列の要素には型がある
31
例) int, char, double など
配列 a
0
1
2
0から始まる番号がついたデータの並び
0から
開始
サイズは
例題2.ベクトルの内積
ベクトル(1.9, 2.8, 3.7)と,ベクトル(4.6,
5.5, 6.4)の内積を表示するプログラムを作る
2つのベクトルの内積の計算のために,サイズ3の一
次元配列を2つ使う
32
例題2:ベクトルの内積
33
#include "stdio.h"
#include <math.h>
int main()
{
int i;
double ip = 0.0;
double u[]={1.9, 2.8, 3.7};
double v[]={4.6, 5.5, 6.4};
int ch;
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
printf("内積=%f¥n", ip);
ch = getchar();
ch = getchar();
return 0;
}
浮動小数を扱う double
34
#include "stdio.h"
#include <math.h>
int main()
{
int i;
double ip = 0.0;
double u[]={1.9, 2.8, 3.7};
double v[]={4.6, 5.5, 6.4};
int ch;
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
printf("内積=%f¥n", ip);
ch = getchar();
ch = getchar();
return 0;
}
変数 u, v は,浮動小数
を要素とする配列で,
サイズは
v
0
1
2
4.6
5.5
6.4
u
0
1
2
1.9
2.8
3.7
メモリ確保および初期化が行われる。
35
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
v
0
1
2
4.6
5.5
6.4
u
0
1
2
1.9
2.8
3.7
ip
0
i
0
0 + 1.9 * 4.6
i = 0 のときは
ip + u[0]*v[0]
i = 0, 1, 2 での繰り返し
(3回繰り返し)
最初は i = 0
36
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
v
0
1
2
4.6
5.5
6.4
u
0
1
2
1.9
2.8
3.7
ip
8.74
i
1
8.74 + 2.8 * 5.5
i = 1 のときは
ip + u[1]*v[1]
i = 0, 1, 2 での繰り返し
(3回繰り返し)
次は i = 1
37
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
v
0
1
2
4.6
5.5
6.4
u
0
1
2
1.9
2.8
3.7
ip
24.14
i
2
24.14 + 3.7 * 6.4
i = 2 のときは
ip + u[2]*v[2]
i = 0, 1, 2 での繰り返し
(3回繰り返し)
次は i = 2
ベクトルの内積
38
繰り返し
1回目
i = 0 i < 3 が成り立つ ip = ip + u[0] * v[0];
繰り返し
2回目
繰り返し
3回目
繰り返し
4回目
i = 1 i < 3 が成り立つ ip = ip + u[1] * v[1];
i = 2 i < 3 が成り立つ ip = ip + u[2] * v[2];
i = 3 i < 3 が成り立たない
i の値
繰り返し条件式
が成り立つか
ip の値
つまり ip の値は u[0]*v[0]
つまり ip の値は u[0]*v[0] + u[1]*v[1]
つまり ip の値は u[0]*v[0] + u[1]*v[1]
+u[2]*v[2]
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
配列の使い方
添字をつけて、普通の変数のように使う。
39
int a[3];
a[0] = i;
a[1] = 5;
fscanf(“%d”,&a[2]);
j = a[2];
printf(“%d %d¥n,a[0],a[1]);
配列 a
0
1
2
添字
a[0]
a[1]
a[2]
次のプログラムを実行してみなさい
40
#include "stdio.h"
#include <math.h>
int main()
{
int i;
double ip = 0.0;
double u[]={1.9, 2.8, 3.7};
double v[]={4.6, 5.5, 6.4};
int ch;
for (i=0; i<3; i++) {
ip = ip + u[i]*v[i];
}
printf("内積=%f¥n", ip);
ch = getchar();
ch = getchar();
return 0;
}
例題3.棒グラフを描く
整数の配列から,その棒グラフを表示するプログ
ラムを作る.
ループの入れ子で,棒グラフの表示を行う
41
例題3:棒グラフ
42
#include "stdio.h"
#include <math.h>
int main()
{
int i;
int j;
int ch;
int a[]={6,4,7,1,5,3,2};
for (i=0; i<7; i++) {
for (j=0; j<a[i]; j++) {
printf("*");
}
printf("¥n");
}
ch = getchar();
ch = getchar();
return 0;
}
配列の宣言
配列からの
読み出し
棒グラフを書く
43
実行結果の例
******
****
*******
*
*****
***
**
多重ループ
ループを入れ子構造にする。
44
外側のループ
内側のループ
for (i=0 ; i < n ;i++)
{
}
for (j=0 ; j < m ;j++)
{
}
i j
0 0
0 1
0 2
0 m-1
1 0
1 1
1 2
1 m-1
n-1 m-1
i=0
i=1
i=n-1