プログラミング論 ファイル処理 (中級編) http://www.ns.kogakuin.ac.jp/~ct13140/ProgC/
高水準と低水準のファイル処理 高水準ファイル処理 ファイルのデータを決められたブロック単位でメモリ(バッファ)に読み込み,バッファからデータ読み込む.バッファを読み切ったら次のデータをファイルからバッファに読む. バッファから処理するため,fscanfなど便利な機能がある. buffered I/Oとも言う. OS間で高い互換性がある. fopen, fread, fgetc, fscanfなど
高水準と低水準のファイル処理 低水準ファイル処理 関数とはしてはバッファせず,要求されたバイトを読み書きする. 実際は,関数とは別にOSがバッファする unbuffered I/Oとも言う. I/O(ファイル読み書き)に関するOS固有の機能を使用できる. 例: Linux固有のDirect I/O OS間で互換性が低い. open, read, lseekなど
高水準 fopen fopen("ファイル名", "モード"); 戻り値: 成功したらファイルへのポインタ, 失敗したら NULL. fopen_s(&fp, "ファイル名", "モード"); 戻り値: 成功したら0. 失敗したら0以外. モード: r, w, a, r+, w+, a+ r:読み込み, w:書き込み, a:追記 r+:読み書き, w+:読み書き, a+:追記読み書き モード: t, b t:テキスト, b:バイナリ
fopenのモード 例: "rt":read text "rb":read binary "r":read text(省略時はtext) "wt":write text "ab":append binary "r+b":read+ binary
fopenのモード モード 説明 ファイルがない場合 ファイルがある場合 "r" 読み込み read エラー(NULLを返す) 既存ファイルから読み込む "w" 書き込み Write 新規作成 既存の内容を破棄し,書き込む "a" 追記 append 既存の内容を保持し,その後ろに追記 "r+" 読み書き read+ 既存の内容を保持し,読み書き "w+" write+ 既存の内容を破棄し,読み書き "a+" 追記読み書き append+ 既存の内容を保持し,追記,読み書き
fopenのモード テキストモード バイナリモード 改行コードのOSごとの差を考慮してread, write read: 改行コード\r\n を \n に変換 Write: \n を 改行コード\r\n に変換 次スライド以降で詳解 バイナリモード 変換なく read, write
文字と文字コード 文字には,"文字コード"という数字が割り当てられている. 計算機はこの文字コード(通常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 は同じ 実行結果 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 は同じ 実行結果 a 97 b 98 c 99 d 100 : x 120 y 121 z 122
改行コード 「改行」に割り当てられている文字コード(改行コード)は,OSにより異なる. Windowsの場合 Linuxの場合 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), 改行
文字を読み込み,表示する プログラム(バイナリモード) #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); "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); "rb"の意味は後述 Visual Studio版
文字を読み込み,表示する プログラム(実行結果) Windowで作った a.txtに対して 65 66 67 13 10 68 69 70 Linuxで作った a.txtに対して 65 66 67 10 68 69 70 a.txt ABC DEF すなわち,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"); 改行コードの変換を行わない
文字を読み込み,表示する プログラム(テキストモード) #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);
文字を読み込み,表示する プログラム(実行結果) Windowで作ったa.txtに対して "rt" 65 66 67 10 68 69 70 "rb" 65 66 67 13 10 68 69 70 a.txt ABC DEF textモードでは, \r \n → \n 13 10 → 10 に変換されて読み込まれる.
文字を書き込むプログラム (テキストモード,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); Windowsにおける 実行結果 a.txt 65 66 67 13 10 68 69 70 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); a.txt 65 66 67 10 68 69 70 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); Linuxにおける 実行結果 a.txt 65 66 67 10 68 69 70 Linuxでは, 改行コードは \n(10)なので, 変換なし.
高水準関数 fputc, fgetc fputc(char c, FILE *fp) fpのファイルにcの文字を書き込む 戻り値:正常時は出力文字コード,エラー時は-1 fgetc(FILE *fp) fpのファイルから1文字読み込む. 戻り値:正常時は取得文字コード,エラー時は-1 ヒント:EOFと-1は同一
高水準関数 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); }
高水準関数 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); }
高水準関数 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); }
高水準関数 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); } 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); } S:\a.txt 23 456 実行結果 i=23
高水準関数 fscanf S:\a.txt HP=23 MP=18 EXP=100 GOLD=99 実行結果 23 18 100 99 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); }
高水準関数 fscanf_s S:\a.txt HP=23 MP=18 EXP=100 GOLD=99 実行結果 23 18 100 99 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); }
高水準関数 fscanf_s S:\a.txt HP=23 MP=18 EXP=100 GOLD=99 実行結果 ret=1 23 18 100 99 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); ret = fscanf_s(fp, "EXP=%d\n", &exp); ret = fscanf_s(fp, "GOLD=%d\n", &gold); fclose(fp); printf("%d %d %d %d\n", hp, mp, exp, gold); }
高水準関数 fscanf_s S:\a.txt HP=23 EXP=100 MP=18 GOLD=99 実行結果 ret=1 ret=0 23 0 100 0 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); ret = fscanf_s(fp, "EXP=%d\n", &exp); ret = fscanf_s(fp, "GOLD=%d\n", &gold); fclose(fp); printf("%d %d %d %d\n", hp, mp, exp, gold); }
高水準ブロック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や負の数)
#include "stdafx. h" #include <stdio. h> void main() { FILE #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); } PNGファイルは, 16バイト目からの4バイトに 画像の横幅が, 20バイト目からの4バイトに 縦の長さが記録されている. <<はビットシフト