csdn の皆さん、こんにちは。C 言語を学習する過程で、さまざまなコードを記述してさまざまなデータを格納できます。プログラムの実行中はデータがメモリに保存されますが、プログラムを閉じるとデータは自然に消えます。今日はデータをファイルに保存する方法についてお話ししたいと思います。
記事ディレクトリ
1. ファイルを使用する理由
先ほど構造を調べたときにアドレス帳のプログラムを書きましたが、アドレス帳を開くとデータがあり、アドレス帳を閉じるとデータは存在しません。書類このことのために、データはメモリに保存はい一時的。
では、データを永続的に保存するにはどうすればよいでしょうか?
これには、データの永続性の問題が関係しています。一般的なデータの永続化方法は次のとおりです。ディスクファイルにデータを保存、データベースに保存等々。
2. ファイルとは
一般に、C ドライブと D ドライブに表示されるものはすべて呼び出すことができます。書類
しかし、プログラミングでは、一般的に次の 2 種類のファイルについて話します。プログラム ファイル、データ ファイル(ファイル機能の観点から分類)。
2.1 プログラムファイル
私たちがC言語で書く.cファイル、オブジェクトファイル(Windows環境のサフィックスは.obj)、実行プログラム(Windows環境のサフィックスは.exe)はすべてプログラムファイルです。
2.2 データファイル
ファイルの内容は、必ずしもプログラムではなく、プログラムがデータを読み取る必要があるファイルや、内容を出力するファイルなど、プログラムの実行時に読み書きされるデータです。
アドレス帳はプログラム ファイルであり、アドレス帳に情報を格納するために使用されるのはデータ ファイルです。
2.3 ファイル名
ユーザーの識別と参照のために、ファイルには一意のファイル識別子が必要です。
ファイル名は次の 3 つの部分で構成されます。ファイル パス + ファイル名トランク + ファイル サフィックス
例: c:\code\test.txt
便宜上、ファイル識別子はしばしばファイル名と呼ばれます。
3. ファイルの開閉
3.1 ファイルポインタ
キャッシュ ファイル システムでは、重要な概念は「ファイル タイプ ポインタ」です。ファイルポインタ
使用された各ファイルは、メモリ内に対応するファイル情報領域を開き、ファイルの関連情報 (ファイルの名前、ファイルの状態、ファイルの現在の場所など) を格納するために使用されます。 )。この情報は構造体変数に格納されます。構造体型はシステムによって宣言され、ファイル
ファイルを開くたびに、システムはファイルの状況に応じて FILE 構造体の変数を自動的に作成し、情報を入力します。ユーザーは
詳細を気にする必要はありません。
通常、この FILE 構造体の変数は、FILE ポインターを介して維持されます。
3.2 ファイルのオープンとクローズ
ファイルは読み書きする前に開き、使用後に閉じる必要があります。
プログラムを作成するとき、ファイルを開くときに、FILE*ポインタ変数が返されてファイルを指します。これは、ポインタとファイルの間の関係を確立することと同じです。
ANSIC では、fopen 関数を使用してファイルを開き、fclose を使用してファイルを閉じることが規定されています。
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
開き方は以下の通り
コードを書いてみよう
int main()
{
FILE* pf=fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
注: ファイルのオープンに失敗した後は、null ポインターが返されるため、判断する必要があります。ファイルポインタNULL ポインターではありません。ファイルを閉じるときに、ポインターも配置しますヌルポインタ。
4. ファイルの順次読み書き
ファイルを開いたり閉じたりする方法がわかったので、ファイルを読み書きするさまざまな方法を紹介しましょう。
4.1 fputc
fputc 関数のパラメータは FILE 型のポインタであり、戻り値は文字を出力するために使用される整数です。
26文字を書いてみよう
注:ファイルに入力したいフォルダがない場合、コンピュータは、書いた.cファイルに新しいファイルを自動的に作成します
int main()
{
FILE* pf = fopen("test.txt", "w");//w为写文件
if (pf == NULL)//判断是否为空指针
{
perror("fopen");
return 1;
}
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a'+i, pf);//fputc写文件
}
fclose(pf);
pf = NULL;
return 0;
}
今すぐ私たちを開きますtxtファイル
だから26文字は正常に保存されましたファイル内
4.2 fgetc
fgetc 関数のパラメーターも FILE 型のポインターであり、戻り値は整数であり、これを使用して文字を読む
アッパーコード
int main()
{
FILE* pf = fopen("test.txt", "r");//r为读文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch;
int i = 0;
for (i = 0; i < 26; i++)
{
ch=fgetc(pf);// fgetchar读文件
printf("%c ", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
実行して、フォルダーに入力したばかりのデータが再度印刷されることを確認しましょう。
4.3 fput
fputs は文字列をファイルに書き込むことができ、関数は指定されたアドレス (str) からコピーを開始し、終端のヌル文字 ('\0') に到達するまで注: fput と put の違いは、ターゲット ストリーム
をfput は他の文字を書き込みませんが、put は最後に自動的に改行を追加します。
アッパーコード
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}//我们想换行直接输入\n即可
char arr[20] = "hello world\n";
fputs(arr, pf);
fputs("thank you\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
このようにして、データを test フォルダーに入力します。
4.4 fgets
fgets はストリームから読み取ることができます文字を読む(num-1)文字が読み取られるまで、または改行またはファイルの終わり、最初に発生した方
アッパーコード
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char arr[20];
fgets(arr, 5, pf);
printf("%s", arr);
fclose(pf);
pf = NULL;
return 0;
}
5 文字を読み取る必要がありましたが、代わりに 4 文字を出力しました。なぜ?
これは、 fgets が最後の桁に \0 を自動的に追加する、したがって num-1 文字のみが出力されます
4.5 fprintf
ストリームを指す C 文字列を形式でストリームに書き込みます
アッパーコード
struct s
{
int a;
float b;
char arr[20];
};
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
struct s s = {
10,3.14f,"hello world" };
fprintf(pf,"%d %f %s", s.a, s.b, s.arr);
fclose(pf);
pf = NULL;
return 0;
}
4.6 fscanf
ストリームからデータを読み取り、パラメーター形式に従って追加パラメーターが指す場所に格納します
アッパーコード
struct s
{
int a;
float b;
char arr[20];
};
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
struct s s;
fscanf(pf,"%d%f%s", &(s.a), &(s.b), s.arr);
printf("%d %f %s", s.a, s.b, s.arr);
fclose(pf);
pf = NULL;
return 0;
}
4.55 ストリームの概念
上記の関数がすべての入力ストリームとすべての出力ストリームに適用できることに気付きましたか?流是什么呢
流
私たちはそれを水の流れと考えることができ、そこから水を取り、水を入れることができます。数据就像一条水流一样
. 将来、ファイルや画面、ネットワーク(外部機器)にデータを書き込む可能性があるので、知識が必要になるかもしれませんが、外部设备
これらはプログラマーには複雑すぎるため、ストリームの概念は抽象化され、ストリームはデータの書き込み方法を見つけます。外部デバイス間へ。プログラマーにとって、データの読み取りはストリームから読み取られ、データの書き込みはストリームに書き込まれることを知っているため、ストリームが外部デバイスとどのように相互作用するかは気にしません。これは抽象出一个流的概念
ファイルを読み書きするとき,文件流
標準出力ストリーム stdout, 標準入力ストリーム stdin, 標準エラーストリーム stderr を含む 1 つを操作します. C 言語プログラムはデフォルトでこれら 3 つのストリームを開きます. 上記の関数はすべての入出力に適用されます
.ストリームは標準 I/O ストリームにも適しています
int main()
{
int ch = fgetc(stdin);
printf("%c", ch);
return 0;
}
4.6 関数セットの比較
scanf/fscanf/sscanf
printf/fprintf/sprintf
4.7 書き込み
fwrite はバイナリ データをストリームに格納できます
struct S
{
char arr[10];
int a;
float b;
};
int main()
{
struct S s = {
"zhangsan",10,2.0f };
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&s, sizeof(s), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
このとき、バイナリ情報が格納されます。
4.8 フレッド
fread はストリーム内のバイナリ情報を読み取ることができます
struct S
{
char arr[10];
int a;
float b;
};
int main()
{
struct S s = {
0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fread(&s, sizeof(s), 1, pf);
printf("%s %d %f", s.arr, s.a, s.b);
fclose(pf);
pf = NULL;
return 0;
}
5. ファイルのランダムな読み書き
5.1 fseek
ファイルポインタの位置とオフセットに従ってファイルポインタを見つけると、ファイルポインタを任意に移動させることができます
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = 0;
ch = fgetc(pf);
printf("%c ", ch);
ch = fgetc(pf);
printf("%c ", ch);
ch = fgetc(pf);
printf("%c ", ch);
ch = fgetc(pf);
printf("%c ", ch);
fseek(pf, -2, SEEK_CUR);
ch = fgetc(pf);
printf("%c ", ch);
fseek(pf, 3, SEEK_SET);
ch = fgetc(pf);
printf("%c ", ch);
fclose(pf);
pf = NULL;
return 0;
}
5.2フィート
開始位置に対するファイル ポインタのオフセットを返します
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
long size;
int ch = 0;
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c\n", ch);
/*fseek(pf, -2, SEEK_CUR);
rewind(pf);*/
printf("%d", ftell(pf));
fclose(pf);
pf = NULL;
return 0;
}
5.3 見つける
ファイルポインタの位置をファイルの先頭に戻す
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
long size;
int ch = 0;
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);
size = ftell(pf);
printf("%ld", size);
fclose(pf);
pf = NULL;
return 0;
}
6. テキストファイルとバイナリファイル
データの編成方法に応じて、データ ファイルが呼び出されます。テキストファイルまたはバイナリファイル.
のデータメモリ内バイナリ形式で保存し、変換せずに外部ストレージに出力した場合はバイナリファイルです。
外部ストレージにASCIIコードで保存する必要がある場合は、変換してから保存する必要があります。ASCII 文字の形式で保存されたファイルは、テキスト ファイルです。
データはどのようにメモリに保存されますか?
キャラクター常に ASCII 形式で保存されます。数値データは、ASCII 形式またはバイナリ形式で保存できます。
整数 10000 がある場合、ASCII コードでディスクに出力するとディスク上で 5 バイト (1 文字につき 1 バイト) を占有し、バイナリ形式で出力すると 1 バイトしか占有しません
。ディスク上に 4 バイト (VS2013 テスト)
テストコード
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
7. ファイル読み込み終了判定
ファイル読み込み終了後に Feof 判定
7.1 誤用されたfeof
注意: ファイルの読み取りプロセス中は、feof 関数の戻り値を使用して、ファイルが終了したかどうかを直接判断することはできません。
代わりに、ファイルの読み取りが終了したときに、読み取りが終了に失敗したか、ファイルの終わりに遭遇したかを判断して適用されます。
- テキストファイルの読み込みが終了したかどうか、戻り値が EOF か ( fgetc )、NULL か ( fgets ) を判断します
例:
fgetc は EOF かどうかを判断します
fgets は戻り値が NULL かどうかを判断します。 - バイナリファイルの読み込み終了判定と、戻り値が実際に読み込む数値より小さいかどうかを判定します。
例:
fread は、戻り値が実際に読み取る数値よりも小さいかどうかを判断します。
正しい使い方:
テキストファイルの例:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
バイナリ ファイルの例:
#include <stdio.h>
enum {
SIZE = 5 };
int main(void)
{
double a[SIZE] = {
1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else {
// error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
8. ファイルバッファ
ANSIC 規格では、データ ファイルを処理するために「バッファ ファイル システム」が採用されています。自動的プログラムで使用されているファイルごとにメモリ内にブロックを作成します」ファイルバッファメモリからディスクへの出力データは、まずメモリ内のバッファに送られ、バッファを満たした後一緒にディスクに送信されます。ディスクからコンピュータにデータを読み込む場合、ディスク ファイルから読み込んだデータをメモリ バッファに入力し(バッファがいっぱい)、バッファからプログラム データ領域(プログラム変数など)にデータを送信します。 。) 一つずつ。バッファーのサイズは、C コンパイル システムによって決定されます。
ここでは、コードでバッファをテストします
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
ここで結論を導き出すことができます:
バッファの存在により、C 言語がファイルを操作する場合、次のことを行う必要があります。フラッシュバッファまたはでファイル操作の終わりファイルを閉じます。
そうしないと、ファイルの読み取りと書き込みに問題が発生する可能性があります
終わり
最初に気に入って、後で見て、習慣にしましょう。! ^ _ ^合言葉は簡単ではありません、皆さんのサポートが私が頑張る原動力です、気に入ったらフォローする
ことを忘れないでください
間違いがありましたら、ご指摘、修正をお願いします(。ì_í。)