コンテンツ
ファイルを使用する理由
先ほど構造を学んだ時、名簿のプログラムを書きました。名簿の実行中は、名簿のデータを追加したり削除したりできます。このとき、データはメモリに保存されます。プログラムが終了すると、名簿は名簿に保存されます。データは当然存在しません。次回名簿プログラムを実行するときに、データを再入力する必要があります。このような名簿を使用するのは非常に不快です。
名簿なので情報を記録しておくべきだと考えており、データを削除することを選択した場合のみ、データは存在しなくなります。
これには、データの永続性の問題が含まれます。一般的なデータの永続化方法には、ディスクファイルへのデータの保存、データベースへのデータの保存などがあります。
ファイルを使用すると、データをコンピューターのハードディスクに直接保存できるため、データが永続的になります。
ファイルとは
ディスク上のファイルはファイルです。しかし、プログラミングでは、一般的に、プログラムファイルとデータファイル(ファイル関数の観点から分類された)の
2種類のファイルについて話します。
プログラムファイル
ソースプログラムファイル(.cのサフィックスが付いている)、オブジェクトファイル(Windows環境では.objのサフィックスが付いている)、および実行可能プログラム(Windows環境では.exeのサフィックスが付いている)が含まれます。
データファイル
ファイルの内容は必ずしもプログラムではありませんが、プログラムがデータを読み取る必要のあるファイルや、内容が出力されるファイルなど、プログラムの実行時に読み書きされるデータです。
ここではデータファイルについて話します。
以前は、処理されたデータの入出力はすべて端末に基づいていました。つまり、データは端末のキーボードから入力され、実行結果がディスプレイに表示されていました。
実際、ディスクに情報を出力し、必要に応じてディスクからメモリにデータを読み込んで使用する場合があります。ディスク上のファイルはここで処理されます。
ファイル名
ファイルには、ユーザーの識別と参照のために一意のファイル識別子が必要です。ファイル名は、ファイルパス+ファイル名トランク+ファイルサフィックス
の3つの部分で構成されます。例: d:\ Ccode \ test.txt便宜上、ファイル識別子はファイル名と呼ばれることがよくあります。
ファイルの開閉
ファイルポインタ
バッファファイルシステムでは、重要な概念は「ファイルポインタ」と呼ばれる「ファイルタイプポインタ」です。
使用される各ファイルは、メモリ内の対応するファイル情報領域を開きます。この領域は、ファイルの関連情報(ファイルの名前、ファイルの状態、ファイルの現在の位置など)を格納するために使用されます。 。この情報は構造変数に格納されます。構造タイプは体系的に宣言され、 FILEという名前が付けられます。
たとえば、VS2013コンパイル環境によって提供されるstdio.hヘッダーファイルには、次のファイルタイプ宣言があります。
struct _iobuf { char* _ptr; int _cnt; char* _base; int _flag; int _file; int _charbuf; int _bufsiz; char* _tmpfname; }; typedef struct _iobuf FILE;
異なるCコンパイラのFILEタイプの内容は完全に同じではありませんが、類似しています。
ファイルが開かれるたびに、システムはファイルの状況に応じてFILE構造の変数を自動的に作成し、情報を入力します。ユーザーは詳細を気にする必要はありません。一般に、このFILE構造体の変数は、より便利なFILEポインターを介して維持されます。
次に、FILE*のポインタ変数を作成できます。
FILE* pf;//文件指针变量
pfを、タイプFILEのデータへのポインター変数として定義します。pfがファイルのファイル情報領域を指すようにすることができます(これは構造変数です)。ファイルには、ファイル情報領域の情報からアクセスできます。つまり、それに関連付けられているファイルは、ファイルポインタ変数を介して見つけることができます。
例えば:
ファイルの開閉
ファイルは読み取りと書き込みの前に開き、使用終了後に閉じる必要があります。
プログラムを作成するとき、ファイルを開くと、ファイルを指すようにFILE *のポインター変数が返されます。これは、ポインターとファイルの関係を確立することにも相当します。
ANSICは、fopen関数を使用してファイルを開き、fcloseを使用してファイルを閉じることを指定します。
ファイルの使用方法
意味 指定したファイルが存在しない場合 「r」(読み取り専用) データを入力するには、既存のテキストファイルを開きます
エラー 「w」(書き込み専用) データを入力するには、テキストファイルを開きます3 新しいファイルを作成する 「a」(追加) テキストファイルの最後にデータを追加します 新しいファイルを作成する 「rb」(読み取り専用) データを入力するには、バイナリファイルを開きます エラー 「wb」(書き込みのみ) データを入力するには、バイナリファイルを開きます 新しいファイルを作成する 「ab」(追加) バイナリファイルの最後にデータを追加します エラー 「r+」(読み取りおよび書き込み) 読み取りと書き込み用のテキストファイルを開きます エラー 「w+」(読み取りおよび書き込み) 読み取りと書き込み用の新しいテキストファイルを作成します 新しいファイルを作成する 「a+」(読み取りおよび書き込み) ファイルの最後で読み取りと書き込みのためにファイルを開きます 新しいファイルを作成する 「rb+」(読み取りおよび書き込み) 読み取りと書き込み用にバイナリファイルを開きます エラー 「wb+」(読み取りと書き込み) 読み取りと書き込みの場合は、新しいバイナリファイルを作成します 新しいファイルを作成する 「ab+」(読み取りと書き込み) ファイルの最後で読み取りと書き込みを行うためにバイナリファイルを開きます 新しいファイルを作成する
int main() { //打开文件 FILE* pf = fopen("test.txt", "r");//当前路径 FILE* pf = fopen("D:\\code\\test.txt", "r");//绝对路径 if (pf == NULL) { perror("fopen"); return 0; } //读文件 //关闭文件 fclose(pf); pf = NULL; return 0; }
ファイルの順次読み取りと書き込み
fputc関数
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //写文件 - 输出操作 //abcd - xyz char ch = 'a'; for (ch = 'a'; ch <= 'z'; ch++) { fputc(ch, pf); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fgetc関数
int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //读文件 - 输入操作 int ch = 0; while ((ch = fgetc(pf)) != EOF)//EOF是文件结束标记,值为-1 { printf("%c ", ch); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fputc関数
#include <stdio.h> int main() { //标准输入流 stdin 键盘 //标准输出流 stdout 屏幕 //标准错误流 stderr 屏幕 int ch = fgetc(stdin); printf("%c\n", ch); fputc(ch, stdout); return 0; }
fputs関数
int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //写文件 - 写一行 fputs("qwerasdzxc", pf); fputs("xxxxxxxxxx\n", pf); fputs("abcdefghij", pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
fgets関数
int main() { char arr[256] = { 0 }; //打开文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //读文件 - 写一行 //fgets(arr, 25, pf);//最多读n-1个字符 //printf("%s", arr); //fgets(arr, 25, pf); //printf("%s", arr); //fgets读取失败会返回NULL while (fgets(arr,256,pf)!=NULL) { printf("%s", arr); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fprintf関数
struct S { char name[20]; int age; double score; }; int main() { struct S s = { "李四",20,87.2 }; //打开文件 FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //写文件 fprintf(pf, "%s %d %lf", s.name, s.age, s.score); //关闭文件 fclose(pf); pf = NULL; return 0; }
fscanf関数
struct S { char name[20]; int age; double score; }; int main() { struct S s = { 0 }; //打开文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //读文件 fscanf(pf, "%s%d%lf", s.name, &(s.age), &(s.score)); printf("%s %d %lf\n", s.name, s.age, s.score); //关闭文件 fclose(pf); pf = NULL; return 0; }
fwrite関数
int main() { struct S s = { "李四",20,87.2 }; //写文件 - 二进制的方式写 FILE* pf = fopen("test.txt", "wb"); if (pf == NULL) { perror("fopen"); return 1; } //二进制的方式写文件 fwrite(&s, sizeof(struct S), 1, pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
フレッド機能
int main() { struct S s = { 0 }; //写文件 - 二进制的方式写 FILE* pf = fopen("test.txt", "rb"); if (pf == NULL) { perror("fopen"); return 1; } //二进制的方式读文件 fread(&s, sizeof(struct S), 1, pf); printf("%s %d %lf", s.name, s.age, s.score); //关闭文件 fclose(pf); pf = NULL; return 0; }
関数のセットを比較します:
scanf / fscanf / sscanf
printf / fprintf / sprintf
標準入力ストリーム(stdin)からのscanf形式の入力関数すべての入力ストリーム用の
fscanf形式の入力関数sscanfは、文字列から形式化されたデータを抽出(変換)できます。printf
は、フォーマットされた出力関数を標準出力ストリーム(stdout)に選択します
。fprintfは
、すべての出力ストリームのフォーマットされた出力関数です
。sprintfは、フォーマット
されたデータを文字列に変換します。
sprintfおよびsscanf関数
struct S { char name[20]; int age; double score; }; int main() { char buf[256] = { 0 }; struct S s = { "李四",20,87.2 }; struct S ret = { 0 }; //将s结构体数据转化为字符串 sprintf(buf, "%s %d %lf", s.name, s.age, s.score); printf("%s\n", buf);//字符串的形式 //从buf字符串从提取结构体数据 sscanf(buf, "%s %d %lf", ret.name, &(ret.age), &(ret.score)); printf("%s %d %lf", ret.name, ret.age, ret.score);//格式化的形式 return 0; }
ファイルのランダムな読み取りと書き込み
fseek関数
位置とオフセットに基づいてファイルポインタを見つけます
int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //随机读 int ch = fgetc(pf); printf("%c\n", ch); ch = fgetc(pf); printf("%c\n", ch); fseek(pf, -1, SEEK_END); ch = fgetc(pf); printf("%c\n", ch); fclose(pf); pf = NULL; return 0; }
int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //随机写 fputc('a', pf); fputc('b', pf); fputc('c', pf); fputc('d', pf); fseek(pf, -3, SEEK_CUR); fputc('w', pf); fclose(pf); pf = NULL; return 0; }
ftell関数
開始位置への現在のファイルポインタのオフセットを計算できます
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //随机写 fputc('a', pf); fputc('b', pf); fputc('c', pf); fputc('d', pf); fseek(pf, -3, SEEK_CUR); fputc('w', pf); long ret = ftell(pf); printf("%ld\n", ret); //关闭文件 fclose(pf); pf = NULL; return 0; }
巻き戻し機能
ファイルポインタの位置をファイルの先頭に戻します
int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //随机写 fputc('a', pf); fputc('b', pf); fputc('c', pf); fputc('d', pf); fseek(pf, -3, SEEK_CUR); fputc('w', pf); long ret = ftell(pf); printf("%ld\n", ret); rewind(pf); ret = ftell(pf); printf("%ld\n", ret); //关闭文件 fclose(pf); pf = NULL; return 0; }
テキストファイルとバイナリファイル
データの編成方法に応じて、データファイルはテキストファイルまたはバイナリファイルと呼ばれます。
データはバイナリ形式でメモリに保存され、変換せずに外部メモリに出力される場合はバイナリファイルになります。
ASCIIコードの形式で外部メモリに保存する必要がある場合は、保存する前に変換する必要があります。ASCII文字で保存されたファイルはテキストファイルです。
データはどのようにメモリに保存されますか?
文字は常にASCII形式で保存され、数値データはASCII形式またはバイナリ形式で保存できます。
整数10000がある場合、ASCIIコードでディスクに出力される場合はディスク上で5バイト(各文字に1バイト)を占有し、バイナリ形式で出力される場合はディスク上で4バイト( 16バイト)のみを占有します。 )。システムストレージ)、は10 27 00 00
ファイル読み取りの終わりの判断
誤用されたfeof
注意:ファイルの読み取りプロセス中、feof関数の戻り値を直接使用して、ファイルが終了したかどうかを判別することはできません。
代わりに、読み取りが失敗したか、ファイルの読み取りが終了したときにファイルの終わりに遭遇したかを判断するために使用されます。
1.テキストファイルの読み取りが終了したかどうか、戻り値がEOF(fgetc)であるかNULL(fgets)であるかを判断します。
例:fgetcはそれがEOFであるかどうかを判断します
。fgetsは戻り値がNULLであるかどうかを判断します。
2.バイナリファイルの読み取り終了を判断し、戻り値が実際に読み取られる数値よりも小さいかどうかを判断します。
例:
freadは、戻り値が実際に読み取られる数よりも少ないかどうかを判断します
int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } int ch = 0; //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((ch = fgetc(pf)) != EOF) // 标准C I/O读取文件循环 { putchar(ch); } //判断是什么原因结束的 if (ferror(pf)) puts("I/O error when reading"); else if (feof(pf)) puts("End of file reached successfully"); //关闭文件 fclose(pf); pf = NULL; return 0; }
ferror関数は、エラーが発生したかどうかを判断して終了します。
ストリームでエラーが発生しなかった場合、ferrorは0を返します。それ以外の場合は、ゼロ以外の値を返します。
feof関数は、ファイルの最後で終了するかどうかを判断します。
現在の位置がファイルの終わりでない場合は0を返します。エラーは返されません。
ファイルの読み取りが失敗した後は、必ず判断してください。!!!
ファイルバッファ
ANSIC標準では、「バッファファイルシステム」を使用してデータファイルを処理します。いわゆるバッファファイルシステムとは、プログラムで使用されているファイルごとに、システムがメモリ内の「ファイルバッファ」を自動的に開くことを意味します。メモリからディスクに出力されたデータは、最初にメモリ内のバッファに送信され、バッファがいっぱいになった後にディスクに送信されます。ディスクからコンピュータにデータを読み込む場合は、ディスクファイルからデータを読み込んでメモリバッファ(フルバッファ)に入力し、からプログラムデータ領域(プログラム変数など)にデータを送信します。バッファを1つずつ。バッファのサイズは、Cコンパイルシステムによって決定されます。
通常の状況では、バッファがいっぱいになると、オペレーティングシステムは、オペレーティングシステムがハードディスク上のバッファにデータを配置することのみを許可します。
#include <stdio.h> #include <windows.h> int main() { FILE* pf = fopen("test.txt", "w"); fputs("abcdef", pf);//先将代码放在输出缓冲区 printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n"); Sleep(10000);//睡眠10秒 printf("刷新缓冲区\n"); fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘) //注:fflush 在高版本的VS上不能使用了 printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n"); Sleep(10000); fclose(pf); //注:fclose在关闭文件的时候,也会刷新缓冲区 pf = NULL; return 0; }