C言語でのファイル操作

序文

なぜファイル操作が必要なのかというと、コードを書くと入力した内容はメモリに保存され、プログラムが終了したり電源が切れたりすると内容も破棄されてしまいます。このように情報を保存することはできず、データの永続化の問題が発生します。一般的なデータの永続化方法には、データをディスク ファイルに保存する、データベースにデータを保存するなどがあります。ファイルを使用すると、データをコンピュータのハード ドライブに直接保存し、データの永続性を実現できます。

メモリとファイルの関係については、次の図で説明します。
ここに画像の説明を挿入します
ファイル操作を学びましょう

1.ファイルとは

ディスク上のファイルはファイルです。
しかし、プログラミングでは通常、プログラム ファイルとデータ ファイル (ファイル機能の観点から分類) という 2 種類のファイルについて話します。
1.1 プログラム ファイル

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境
后缀为.exe)。

1.2 データファイル

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,
或者输出内容的文件。

ファイル操作を学習する前は、キーボードから入力してから画面に出力していました。これではデータを永続化できません。これで、データをハードディスクに書き込んで、ハードディスクにデータを保存できるようになります。 . プログラムにデータが入力されます。これにより、データの永続化も実現できます。
1.3 ファイル名
ユーザーの識別と参照を容易にするために、ファイルには一意のファイル ID が必要です。
ファイル名には、ファイル パス + ファイル名のトランク + ファイル接尾辞の 3 つの部分が含まれます

D:\bite study code\task\task_10_12\text.c
//文件路径:D:\bite study code\task\task_10_12
//文件主干:text
//文件后缀:.c

便宜上、ファイル識別子をファイル名と呼びます

2. ファイルの開閉

2.1 ファイル ポインタ
バッファ ファイル システムでは、重要な概念は「ファイル ポインタ」と呼ばれる「ファイル タイプ ポインタ」です。
使用される各ファイルは、メモリ内に対応するファイル情報領域を開きます。この領域は、ファイルの関連情報 (ファイル名など) を保存するために使用されます。
、ファイルのステータスとファイルの現在の場所など)。この情報は構造体変数に保存されます。構造体のタイプはシステムによって宣言され、
、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;

コンパイラが異なれば、FILE 構造の内容も異なります。
ユーザーがファイルを開くたびに、オペレーティング システムが FILE 構造体の変数を自動的に作成し、構造体の内容を埋め込みます。ユーザーはこれについて心配する必要はありません。
ファイルに何かを書き込んだり、ファイルに何かを取得したいときは、ファイル情報領域を見つけることができるアドレスのみが必要で、その後、ファイル情報領域を使用してこのファイルにアクセスします。 。
次のようなアドレス (FILE* ポインタ変数) を作成します。

FILE* pf;//文件指针变量

は、FILE 型データを指すポインタ変数として pf を定義します。 pf に特定のファイルのファイル情報領域を指すようにすることができます(構造体変数です)。ファイル情報領域の情報を介してファイルにアクセスできます。つまり、それに関連付けられたファイルは、ファイル ポインター変数を通じて見つけることができます。
例:
ここに画像の説明を挿入します
2.2 ファイルのオープンとクローズ
ファイルは読み取りと書き込みの前にオープンする必要があり、また、オープンする必要があります。使用後はファイルを閉じてください。
プログラムを作成するとき、ファイルを開くときに、ファイルを指す FILE* ポインタ変数が返されます。これは、
ポインタとファイルを確立するのと同じです。関係。
ANSIC では、ファイルを開くには fopen 関数を使用し、ファイルを閉じるには fclose を使用することが規定されています。

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

次のように開きます:
ここに画像の説明を挿入します
コード例:

#include <stdio.h>
int main ()
{
    
    
  FILE * pFile;
  //打开文件
  pFile = fopen ("myfile.txt","w");
  //文件操作
  if (pFile!=NULL)
 {
    
    
    fputs ("fopen example",pFile);
    //关闭文件
    fclose (pFile);
 }
  return 0;
  }

パスの問題について話しましょう。ファイルを開くとき、const char * filename はファイルの名前を指します。デフォルトでは、ファイルはプログラムの現在のパスにあります。このファイルを上位レベル以上の場合、このファイルにアクセスしようとすると、ファイルが見つからないことが表示されます。
パス:
1. 相対パス

.\\当前路径
..\\上一级路径

プログラム内で \ がエスケープを意味するため、\ の代わりに \ を使用する理由
2. 絶対パス

D:\\bite study code\\task\\task_10_11\\task_10_11\\data.txt   绝对路径

実際の列のコード:

#include<stdio.h>
int main()
{
    
    
	//打开文件
	/*FILE* pf=fopen("data.txt", "w");*/
	//相对路径
	//  .\\指当前路径  
	//   ..\\指上一个路径


	/*FILE* pf = fopen(".\\..\\data.txt", "r");*///指当前路径的上一个路径   

	/*FILE* pf = fopen("..\\..\\data.txt", "r");*///指当前路径的上一个路径的上一个路径


	//绝对路径

	FILE* pf = fopen("D:\\bite study code\\task\\task_10_11\\task_10_11\\data.txt", "r");//这个就是从根找,就是绝对路径

	if (pf == NULL)
	{
    
    
		perror("fopen");
		return;
	}
	//写文件



	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

3. ファイルのシーケンシャル読み取りと書き込み

ここに画像の説明を挿入します
次のカテゴリに分類します。
文字クラス: 文字入力関数 (fgetc)、文字出力関数 (fputc)
テキスト行 (string) ) : テキスト行入力関数 (fgets) テキスト出力関数 (fputs)
書式設定 (整数、浮動小数点) : 書式付き入力関数 (fscanf) 書式付き出力関数 (fprintf) a>
バイナリ: バイナリ入力 (fread) バイナリ出力 (fwrite)

これらの機能を紹介する前に、まずストリームの概念について説明します。ストリームの概念自体は非常に抽象的なものです。すべてのストリームは 2 つのタイプに分けられます。1 つは標準ストリーム、もう 1 つはファイルですストリーム。 。
標準ストリーム:
1. 標準入力ストリーム: stdin (キーボードからの入力)
2. 標準出力ストリーム: stdout(画面に印刷)
ファイル ストリーム:
1. ファイル入力ストリーム: ファイルの内容を読み取り、メモリに入力します。
2. ファイル出力ストリーム: インメモリ プログラムを通じてファイルに出力

文字クラス:
文字出力関数 (fputc)

int fputc ( int character, FILE * stream );

int 文字: メモリからファイルに出力する必要がある文字。ここでの文字は整数プロモートされ、内部で符号なし文字に変換される必要があります。
FILE * stream: ポインタ出力ストリームの FILE オブジェクトを識別するポインター (実際には、ファイル情報領域を指すアドレスに出力し、ファイル情報領域の情報を通じてコン​​テンツをファイルに書き込みます)
戻り値の型: int
戻りが成功した場合、戻り値は文字の ASCII コード値になります。
戻りが失敗した場合、戻り値は次のようになります。 EOF (-1) を使用して、上記で返された型を確認します。
コードの実装:

#include<stdio.h>
int main()
{
    
    
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//写文件
	fputc('a', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

ファイルに表示される内容:
ここに画像の説明を挿入します
26 文字を印刷する必要があります:

int main()
{
    
    
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//写文件
	char ch;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
    
    
		fputc(ch, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

ファイル内に表示されるもの:
ここに画像の説明を挿入します
文字出力関数 (fgetc)

int fgetc ( FILE * stream )

FILE * stream: 入力ストリーム (ファイルの内容をメモリに入力) を識別する FILE オブジェクトへのポインタ
戻り値の型: int
戻り値: 戻りが成功した場合、返された文字は整数化され、ASCII コード値としてメモリに返されます。
戻りが失敗した場合は、EOF を返します。
コードの実装:

int main()
{
    
    
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	//判断是否打开成功
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//读文件
	int ch=fgetc(pf);
	printf("%c", ch);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 

画面に出力します:
ここに画像の説明を挿入します
テキスト クラス:
テキスト出力関数 (fputs)

int fputs ( const char * str, FILE * stream );

const char * str: 出力される文字列
FILE * stream: 出力ストリームを識別する FILE オブジェクトへのポインタ
戻り値の型: int
戻り値: 成功した場合は負でない値を返します。
失敗した場合は EOF を返します。
コードの実装:

int main()
{
    
    
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//写文件
	fputs("abcdefg", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

ファイル内に表示されるもの:
ここに画像の説明を挿入します
テキスト入力関数 (fgets)

char * fgets ( char * str, int num, FILE * stream );

char * str: ターゲットにコピーする必要がある文字配列へのポインタ
int num: str にコピーされる文字の最大数 (改行文字が入力されるとコピーは停止します)ターゲットの最大文字配列は 100 ですが、コピーできるのは 99 文字のみであると仮定します)
FILE * stream: 入力ストリームを識別する FILE オブジェクトへのポインタ。
戻り値の型: char*
戻り値: 戻りが成功すると、str 配列の先頭アドレスが返されます。
戻りが失敗した場合は、ヌル ポインタが返されます。(NULL)
コードの実装:

int main()
{
    
    
	char arr[5] = {
    
     0 };
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	//判断是否打开成功
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//读文件
	fgets(arr, 5, pf);
	printf("%s", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

画面に印刷する:
ここに画像の説明を挿入します
ファイルに改行文字を追加する場合:
ここに画像の説明を挿入します
スペースがあった場合でも、スペースを印刷できます。 :
ここに画像の説明を挿入します

フォーマット:
フォーマット出力関数 (fprintf)

int fprintf ( FILE * stream, const char * format, ... );

FILE * stream: 出力ストリームを識別する FILE オブジェクトへのポインタ。
const char * format: printf 関数の説明を比較します。
整数データを出力するとします。
printf 関数printf( "%d",a);
fprintf function fprintf( FILE * stream, "%d", a);
戻り値の型: int コードの実装: 戻りに失敗した場合は、負の数が返されます。
戻り値: 成功した場合は、書き込まれた総文字数が返されます。

struct stu
{
    
    
	char name[20];
	int age;
	float hight;
};
int main()
{
    
    
	 struct stu s = {
    
     "zhangsan",20,175.00 };
	 //打开文件
	 FILE* pf=fopen("data.txt", "w");
	 if (pf == NULL)
	 {
    
    
		 perror("fopen");
	 }
	 //写文件
	 fprintf(pf, "%s-%d-%f", s.name, s.age, s.hight);
	 //关闭文件
	 fclose(pf);
	 pf = NULL;
	return 0;
}

ファイルに表示される内容:
ここに画像の説明を挿入します
フォーマット入力関数 (fscanf)

int fscanf ( FILE * stream, const char * format, ... );

FILE * stream: データの読み取り元の入力ストリームを識別する FILE オブジェクトへのポインタ
戻り値の型: int
コードの実装:

struct stu
{
    
    
	int age;
	float hight;
	char name[20];
};
int main()
{
    
    
	struct stu s = {
    
    0};
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//读文件
	fscanf(pf, "%d-%f-%s", &(s.age), &(s.hight),s.name);
	printf("%s-%d-%f", s.name, s.age, s.hight);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

画面に印刷:
ここに画像の説明を挿入します
バイナリ関数:
バイナリ出力関数 (fwrite)

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

const void * ptr: 書き込まれる要素配列のアドレスを指します。
size_t size: コピーする必要がある各要素のサイズ (バイト単位) コードの実装: FILE * stream: 指定された出力ストリームの FILE オブジェクトへのポインタ。
size_t count: コピーされる要素の数

int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	//打开文件
	FILE* pwrite = fopen("data.txt", "wb");
	if (pwrite == NULL)
	{
    
    
		perror("fopen");
	}
	//读文件
	fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pwrite);
	//关闭文件
	fclose(pwrite);
	pwrite = NULL;
	return 0;
}

ファイルの下を見てください:
ここに画像の説明を挿入します
は理解できない文字化けの文字列です。これは、バイナリ形式でファイルに保存されているためです。ファイル内では理解できませんが、理解できる可能性があります。メモリに変換され、画面に印刷されます。これをメモリに変換するにはバイナリ入力関数(fread)が必要になるので、次にこの関数を学習していきます。

バイナリ入力関数(fread)

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

void * ptr: 要素配列にコピーされるアドレスへのポイント
size_t size: コピーされる各要素のサイズ (バイト単位)
size_t count: コピーする要素の数
FILE * stream: 指定された入力ストリームの FILE オブジェクトへのポインタ。
戻り値の型: size_t
コード実装:

int main()
{
    
    
	int arr[10] = {
    
    0};
	//打开文件
	FILE* fwrite=fopen("data.txt", "rb");
	if (fwrite == NULL)
	{
    
    
		perror("fopen");
	}
	//读文件
	fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), fwrite);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	//关闭文件
	fclose(fwrite);
	fwrite = NULL;
	return 0;
}

画面に印刷:
ここに画像の説明を挿入します

4 ファイルのランダムな読み取りと書き込み

4.1fseek関数

int fseek ( FILE * stream, long int offset, int origin );

FILE * stream: ストリームを識別する FILE オブジェクトへのポインタ
long int offset: オフセット
intorigin: ファイル カーソルの位置< /span> コード例:今回は: ファイルカーソルがファイルポインタではない ファイルポインタが動かない かなりのガイドです。ファイル カーソルが移動し、そのコンテンツにアクセスすると、カーソルがそこを指します。 SEEK_END ファイルの末尾 SEEK_CUR ファイル カーソルの現在位置
SEEK_SET ファイルの先頭



#include<stdio.h>
int main()
{
    
    
	char arr[10] = {
    
     0 };
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
    
    
		perror("fopen");
	}
	//随机访问文件的内容
	fseek(pf, 2, SEEK_SET);
	//读文件
	fgets(arr, 10, pf);
	printf("%s", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

設定条件は、ファイル カーソルのファイルの開始オフセットを 2 に設定することです
画面に印刷します:
ここに画像の説明を挿入します
4.2fetll (returnファイルへの開始位置を基準としたポインタのオフセット)

long int ftell ( FILE * stream );

例:

#include <stdio.h>
int main ()
{
    
    
  FILE * pFile;
  long size;
  pFile = fopen ("myfile.txt","rb");
  if (pFile==NULL) perror ("Error opening file");
  else
 {
    
    
    fseek (pFile, 0, SEEK_END);   // non-portable
    size=ftell (pFile);
    fclose (pFile);
    printf ("Size of myfile.txt: %ld bytes.\n",size);
 }
  return 0;
}

4.3rewind(ファイルポインタの位置をファイルの開始位置に戻す)

void rewind ( FILE * stream );

例:

#include <stdio.h>
int main ()
{
    
    
  int n;
  FILE * pFile;
  char buffer [27];
  pFile = fopen ("myfile.txt","w+");
  for ( n='A' ; n<='Z' ; n++)
    fputc ( n, pFile);
  rewind (pFile);
  fread (buffer,1,26,pFile);
  fclose (pFile);
  buffer[26]='\0';
  puts (buffer);
    return 0;
}

5. テキストファイルとバイナリファイル

データの編成方法に応じて、データ ファイルはテキスト ファイルまたはバイナリ ファイルと呼ばれます。
データはメモリ内にバイナリ形式で保存されており、そのまま外部メモリに出力するとバイナリファイルとなります。
外部ストレージに ASCII コードで保存する必要がある場合は、保存する前に変換する必要があります。 ASCII 文字の形式で保存されたファイルはテキスト ファイルです。

6. ファイル読み込み終了の判定

6.1 feof の誤った使用
注意: ファイル読み取りプロセス中、feof 関数の戻り値を使用してファイルが終了したかどうかを直接判断することはできません。
代わりに、ファイルの読み取りが終了したときに、読み取りが失敗したか、ファイルの終わりに達したかを判断するために使用されます。

  1. テキスト ファイルの読み取りが完了したかどうか、戻り値が EOF (fgetc) であるか NULL (fgets) であるかを判断します。
    例:
    fgetc は EOF かどうかを判断します。
    fgets は戻り値が NULL かどうかを判断します。
  2. バイナリファイルの読み込み終了判定は、戻り値が実際に読み込む数値より小さいかどうかを判定します。
    例:
    fread は、戻り値が実際に読み取られる数値より小さいかどうかを判断します。
    ####### 7. ファイル キャッシュ領域

7. ファイルキャッシュ領域

ANSIC 標準では、「バッファ ファイル システム」を使用してデータ ファイルを処理します。いわゆるバッファ ファイル システムとは、システムがプログラムで使用されているファイルごとにメモリ内に「ファイル バッファ」を自動的に開くことを意味します。 。メモリからディスクに出力されたデータは、まずメモリ内のバッファに送られ、バッファがいっぱいになった後にまとめてディスクに送られます。ディスクからコンピュータにデータを読み取る場合、データはディスク ファイルから読み取られてメモリ バッファに入力され (バッファがいっぱいになります)、その後データがバッファからプログラム データ領域 (プログラム変数、など)一つ一つ。バッファのサイズは C コンパイル システムによって決定されます。
ここに画像の説明を挿入します
ここで結論を導き出すことができます:
バッファが存在するため、C 言語はファイルを操作するときにバッファを更新するか、ファイルを終了する必要があります。ファイル操作。ファイルを閉じるとき。
これを行わないと、ファイルの読み取りと書き込みで問題が発生する可能性があります。

この章の終わり…

おすすめ

転載: blog.csdn.net/HD_13/article/details/133807993