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から
開始
サイズは
3
例題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 は,浮動小数
を要素とする配列で,
サイズは3
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