プログラミング論
ファイル処理 (中級編)
http://www.ns.kogakuin.ac.jp/~ct13140/ProgC/
高水準と低水準のファイル処理
• 高水準ファイル処理
–
ファイルのデータを決められたブロック単位でメモリ(バッファ)に読み込み,バッファからデータ読み込む.
バッファを読み切ったら次のデータをファイルからバッ ファに読む.
–
バッファから処理するため,fscanfなど便利な機能 がある.– buffered I/Oとも言う.
– OS間で高い互換性がある.
– fopen, fread, fgetc, fscanfなど
D-2
高水準と低水準のファイル処理
• 低水準ファイル処理
–
関数とはしてはバッファせず,要求されたバイトを読み 書きする.•実際は,関数とは別にOSがバッファする
– unbuffered I/Oとも言う.
– I/O(ファイル読み書き)に関するOS固有の機能を使
用できる.•例: Linux固有のDirect I/O
– OS
間で互換性が低い. – open, read, lseekなど
D-3
高水準 fopen
fopen("ファイル名", "モード");
戻り値: 成功したらファイルへのポインタ,
失敗したら
NULL.
fopen_s(&fp, "ファイル名", "モード");
戻り値: 成功したら0. 失敗したら0以外.
モード: r, w, a, r+, w+, a+
r:読み込み, w:書き込み, a:追記
r+:読み書き, w+:読み書き, a+:追記読み書き
モード: t, bt:テキスト, b:バイナリ
D-4
fopen のモード
例:
"rt":read text
"rb":read binary
"r":read text( 省略時は text)
"wt":write text
"ab":append binary
"r+b":read+ binary
D-5
fopen のモード
D-6
モード 説明 ファイルがない場合 ファイルがある場合
"r" 読み込み read
エラー(NULLを返す) 既存ファイルから読み込む
"w" 書き込み Write
新規作成 既存の内容を破棄し,書き込む
"a" 追記 append
新規作成 既存の内容を保持し,その後ろに 追記
"r+" 読み書き read+
エラー(NULLを返す) 既存の内容を保持し,読み書き
"w+" 読み書き write+
新規作成 既存の内容を破棄し,読み書き
"a+" 追記読み書き append+
新規作成 既存の内容を保持し,追記,読み 書き
fopen のモード
• テキストモード
–
改行コードのOS
ごとの差を考慮してread, write – read: 改行コード\r\n を \n に変換
– Write: \n を 改行コード\r\n に変換 –
次スライド以降で詳解• バイナリモード
–
変換なくread, write
D-7 O-8
文字と文字コード
• 文字には, " 文字コード " という数字が割り当てら れている.
• 計算機はこの文字コード(通常ASCIIコード)を 用いて文字を処理する.
文字 '0' '1' '2' '3' '4' 'A' 'B' 'C' 'a' 'b' 文字
コード 16進数
30 31 32 33 34 41 42 43 61 62
文字 コード 10進数
48 49 50 51 52 65 66 67 97 98
文字コード表示プログラム (1/2)
void main(){
char ch;
for(ch='A'; ch<='Z'; ch++){
printf("%c %d\n", ch, ch);
} }
ヒント:'A'と65は同じ.
ch='A' と ch=65 は同じ.
ch<='Z' と ch<=90 は同じ
D-9 実行結果 A 65 B 66 C 67 D 68 : X 88 Y 89 Z 90
文字コード表示プログラム (2/2)
void main(){
char ch;
for(ch='a'; ch<='z'; ch++){
printf("%c %d\n", ch, ch);
} }
ヒント:'a'と97は同じ.
ch='a' と ch=97 は同じ.
ch<='z' と ch<=122 は同じ
D-10 実行結果 a 97 b 98 c 99 d 100 : x 120 y 121 z 122
改行コード
•
「改行」に割り当てられている文字コード(改行コード)は,OSにより異なる.
– Windowsの場合
• 10進数の13, 10の2バイト. CR+LFの2バイト.
(16進数で0D,0Aの2バイト)
– Linuxの場合
• 10進数の10の1バイト. LFの1バイト
–
旧Mac OSの場合• 10進数の13の1バイト. CRの1バイト
• CR: carriage return, \r, 13(10), 0D(16), 復帰
• LF: line feed, \n, 10(10), 0A(16), 改行
D-11
文字を読み込み,表示する プログラム (バイナリモード)
#include <stdio.h>
void main(){
FILE *fp; int ch;
fp = fopen("S:\\a.txt","rb");
if( fp == NULL ){
(略) }
while( (ch=fgetc(fp)) != -1 ){
printf("%d\n", ch);
}
fclose(fp);
} D-12
"rb"の意味は後述
文字を読み込み,表示する プログラム (バイナリモード)
#include <stdio.h>
void main(){
FILE *fp; int ch, ret;
ret = fopen_s( &fp, "s:\\a.txt", "rb");
if( ret != 0 ) { (略)
}
while( (ch=fgetc(fp)) != -1 ){
printf("%d\n", ch);
}
fclose(fp);
} D-13
"rb"の意味は後述 Visual Studio版
文字を読み込み,表示する プログラム ( 実行結果 )
Windowで作った a.txt
に対して65
66 67 13 10 68 69
70
D-14Linuxで作った a.txt
に対して65
66 67 10 68 69 70
ABC DEF a.txt
すなわち,Windowsで作った
「ABC改行DEF」のファイルと,
Linuxで作ったファイルでは,
内容が異なる
fopen
テキストモードとバイナリモード
• テキストモード
fopen("S:a.txt", "rt");
fopen("S:a.txt", "wt");
改行コードの変換を行う
• バイナリモード
fopen("S:a.txt", "rb");
fopen("S:a.txt", "wb");
改行コードの変換を行わない
D-15
文字を読み込み,表示する プログラム (テキストモード)
#include <stdio.h>
void main(){
FILE *fp; int ch, ret;
ret = fopen_s( &fp, "s:\\a.txt", "rt");
if( ret != 0 ) { (略)
}
while( (ch=fgetc(fp)) != -1 ){
printf("%d\n", ch);
}
fclose(fp);
} D-16
文字を読み込み,表示する プログラム ( 実行結果 )
"rt"
65 66 67 10 68 69 70
D-17
"rb"
65 66 67 13 10 68 69 70
ABC DEF a.txt
textモードでは,
\r \n → \n 13 10 → 10
に変換されて読み込まれる.
Window で作った a.txt に対して
文字を書き込むプログラム ( テキストモード ,Windows)
#include "stdafx.h"
#include <stdio.h>
void main() { FILE *fp;
int ch, ret;
ret = fopen_s( &fp, "s:\\a.txt", "wt");
if (ret != 0 ) { (略)
}
fprintf( fp, "ABC\nDEF");
fclose(fp);
}
D-18
65 66 67 13 10 68 69 70 Windowsにおける
実行結果a.txt
textモードでは,
\n → \r \n 10 → 13 10 に変換されて 書き込まれる.
文字を書き込むプログラム (バイナリモード)
#include "stdafx.h"
#include <stdio.h>
void main() { FILE *fp;
int ch, ret;
ret = fopen_s( &fp, "s:\\a.txt", "wb");
if (ret != 0 ) { (略)
}
fprintf( fp, "ABC\nDEF");
fclose(fp);
}
D-19
65 66 67 10 68 69 70 a.txt
binaryモードでは,
\n → \r \n 10 → 13 10 の変換をしない.
文字を書き込むプログラム (テキストモード,Linux)
#include <stdio.h>
void main() { FILE *fp;
int ch, ret;
fp = fopen( "a.txt", "wt");
if( fp == NULL ){
(略) }
fprintf( fp, "ABC\nDEF");
fclose(fp);
}
D-20
65 66 67 10 68 69 70 Linuxにおける
実行結果a.txt
Linuxでは,
改行コードは
\n(10)なので,
変換なし.
高水準関数 fputc, fgetc
fputc(char c, FILE *fp) fpのファイルにcの文字を書き込む
戻り値:正常時は出力文字コード,エラー時は-1 fgetc(FILE *fp)
fp のファイルから 1 文字読み込む.
戻り値 : 正常時は取得文字コード,エラー時は -1
ヒント:EOFと-1は同一
D-21
高水準関数 fputs
fputs(char *s, FILE *fp);
fpのファイルに,文字列sを書き込む.
void main() { FILE *fp;
int ret;
ret = fopen_s( &fp, "s:\\a.txt", "wt");
if( ret != 0 ){ (略) } fputs( "ABC\nDEF", fp);
fclose(fp);
}
D-22
高水準関数 fgets
fgets(char *buf, int n, FILE *fp);
fp のファイルから, buf に対して ,
n文字読み込む
(n-1個の文字と,1個の終端記号) void main() {char buf[100]; FILE *fp; int ret;
ret = fopen_s(&fp, "s:\\a.txt", "rt");
if( ret != 0 ){ (略) } fgets(buf, 100, fp);
printf("%s\n", buf);
fclose(fp);
}
D-23
高水準関数 fprintf
fprintf(FILE *fp, char s, ...);
fp のファイルに,文字列 s を書き込む .
void main() { FILE *fp;
int ret;
ret = fopen_s(&fp, "s:\\a.txt", "wt");
if( ret != 0 ){ (略) }
fprintf(fp, "ret=%d\n", ret);
fclose(fp);
}
D-24
高水準関数 fscanf
fscanf(FILE *fp, char s, ...);
fpのファイルから,文字列sに従い読み込む.
戻り値 : 正常時は代入された個数 . エラー時は -1
void main() {
FILE *fp; int ret, i;
fp = fopen("s:\\a.txt", "rt");
if( fp == NULL ){ (略) } fscanf(fp, "%d", &i);
fclose(fp);
printf("i=%d\n", i);
} D-25
S:\a.txt 23
456
実行結果 i=23
高水準関数 fscanf_s
fscanf(FILE *fp, char s, ...);
fpのファイルから,文字列sに従い読み込む.
戻り値 : 正常時は代入された個数 . エラー時は -1
void main() {
FILE *fp; int ret, i;
ret = fopen_s(&fp,"s:\\a.txt","rt");
if( ret != 0 ){ (略) } fscanf_s(fp, "%d", &i);
fclose(fp);
printf("i=%d\n", i);
} D-26
S:\a.txt 23
456
実行結果 i=23
高水準関数 fscanf
void main() {
FILE *fp; int ret, hp, mp, exp, gold;
fp = fopen( "S:\\a.txt", "rb");
if ( fp == NULL ){ (略) } fscanf(fp, "HP=%d\n", &hp);
fscanf(fp, "MP=%d\n", &mp);
fscanf(fp, "EXP=%d\n", &exp);
fscanf(fp, "GOLD=%d\n", &gold);
fclose(fp);
printf("%d %d %d %d\n", hp, mp, exp, gold);
}
D-27 S:\a.txt HP=23 MP=18 EXP=100 GOLD=99
実行結果 23 18 100 99
高水準関数 fscanf_s
void main() {
FILE *fp; int ret, hp, mp, exp, gold;
ret = fopen_s(&fp, "s:\\a.txt", "rb");
if (ret != 0) { (略) } fscanf_s(fp, "HP=%d\n", &hp);
fscanf_s(fp, "MP=%d\n", &mp);
fscanf_s(fp, "EXP=%d\n", &exp);
fscanf_s(fp, "GOLD=%d\n", &gold);
fclose(fp);
printf("%d %d %d %d\n", hp, mp, exp, gold);
}
D-28 S:\a.txt HP=23 MP=18 EXP=100 GOLD=99
実行結果 23 18 100 99
高水準関数 fscanf_s
void main() {
FILE *fp; int ret, hp=0, mp=0, exp=0, gold=0;
ret = fopen_s(&fp, "s:\\a.txt", "rb");
if (ret != 0) { (略) }
ret = fscanf_s(fp, "HP=%d\n", &hp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "MP=%d\n", &mp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "EXP=%d\n", &exp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "GOLD=%d\n", &gold);
printf("ret=%d\n", ret);
fclose(fp);
printf("%d %d %d %d\n", hp, mp, exp, gold);
}
D-29 S:\a.txt HP=23 MP=18 EXP=100 GOLD=99
実行結果 ret=1 ret=1 ret=1 ret=1
23 18 100 99
高水準関数 fscanf_s
void main() {
FILE *fp; int ret, hp=0, mp=0, exp=0, gold=0;
ret = fopen_s(&fp, "s:\\a.txt", "rb");
if (ret != 0) { (略) }
ret = fscanf_s(fp, "HP=%d\n", &hp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "MP=%d\n", &mp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "EXP=%d\n", &exp);
printf("ret=%d\n", ret);
ret = fscanf_s(fp, "GOLD=%d\n", &gold);
printf("ret=%d\n", ret);
fclose(fp);
printf("%d %d %d %d\n", hp, mp, exp, gold);
}
D-30 S:\a.txt HP=23 EXP=100 MP=18 GOLD=99
実行結果 ret=1 ret=0 ret=1 ret=0 23 0 100 0
高水準ブロック read,write
fread(char *buf, int size, int n, FILE *fp);
fpのファイルから,bufに,sizeバイト*n個分読み込む.
戻り値:読み込んだ個数.(バイト数でなく,0~nの個数) fwrite(char *buf, int size, int n, FILE *fp);
bufから,fpのファイルに,sizeバイト*n個分書き込む.
戻り値:書き込んだ個数.(バイト数でなく,0~nの個数) fseek(FILE *fp, long n, int whence)
fpのファイルのnの場所に移動.
whenceがSEEK_SETなら,ファイルの先頭からnバイト目.
SEEK_CURなら,現在の位置からnバイト目.
SEEK_ENDなら,ファイルの終端からnバイト目.(nは0や負の数) D-31
#include "stdafx.h"
#include <stdio.h>
void main() {
FILE *fp; int ret;
unsigned int width, height;
unsigned char buf[8]; //8バイトの配列 ret = fopen_s(&fp, "s:\\a.png", "rb");
if (ret != 0) { (略) }
fseek(fp, 16, SEEK_SET); //ファイルの16バイト目に移動 fread(buf, 4, 2, fp); //その場所から,4*2=8バイト読み込み fclose(fp);
printf("%02x %0x2 %02x %02x\n", buf[0], buf[1], …);
printf("%02x %0x2 %02x %02x\n", buf[4], buf[5], …);
width = (buf[0]<<24U) + (buf[1] << 16U) + (buf[2] << 8U) + (buf[3]);
height = (buf[4] << 24U) + (buf[5] << 16U) + (buf[6] << 8U) + (buf[7]);
printf("%u * %u\n", width, height);
} D-32
PNGファイルは,
16バイト目からの4バイトに 画像の横幅が,
20バイト目からの4バイトに 縦の長さが記録されている.
<<はビットシフト