一篇文章理解C语言文件 *** 作

一篇文章理解C语言文件 *** 作,第1张

1、为什么使用文件?

使用文件可以将数据直接存放在电脑的硬盘上,做到了数据的持久化存在。


2、什么是文件?

磁盘上的文件是文件
在程序设计中,一般文件分为程序文件和数据文件(是从文件功能的角度来分类的)

3、文件的打开和关闭 1、文件指针

在C语言中每一个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息。


这些信息保存在一个结构体变量中,该结构体的类型是由系统声明的,为FILE。



每当打开一个文件时候,系统会根据文件的情况自动创建一个FILE结构变量,并填充其中的信息,我们并不需要去关心其中的细节,一般我们都是通过一个FILE的指针来维护FILE这个结构的变量,这样的话我们使用起来将会更加的方便。


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

定义的pf是一个指向FILE类型数据的指针变量。


可以使pf指向某个文件的文件信息区,通过其就能访问该文件。


2、文件的打开和关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

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

上面的两个函数就是用来打开文件和关闭文件的
那么具体应该怎么使用呢?
首先打开文件fopen这个函数第一个个变量为一个地址,就是所需要打开文件的路径,第二个形参为文件的使用方式,文件的使用方式一般有以下几种

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,新建一个文本文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件
#include
int main()
{
	//打开文件
	FILE* pf = fopen("bit.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	//文件 *** 作
	// 
	// 
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在没有运行打开文件的代码之前,我们的路径之下没有bit.txt这个文件的

当我们运行之后
这里就创建出了一个bit.txt的文件。


当然这里的路径我们也可以写一个绝对路径,此时我们可以在硬盘的任何位置创建出bit.txt文件。


4、文件的顺序读写
功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

我们来一一了解一下这些函数该怎么使用
1、fputc
它的函数模型是这样子的

int fputc(int c,FILE* stream)

它一次只能完成一个字符的输出
我们来看看怎么使用的的,上面的代码我们创建了一个bit.txt文件

#include
int main()
{
	//打开文件
	FILE* pf = fopen("bit.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	//文件 *** 作
	fputc('h', pf);
	fputc('e', pf);
	fputc('l', pf);
	fputc('l', pf);
	fputc('o', pf);
	fputc(' ', pf);
	fputc('b', pf);
	fputc('i', pf);
	fputc('t', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

我们向bit.txt文件中写入hello bit

fputc

我们看到了向文件中写入了“hello bit"。



2、fgetc
它的函数模型是这样子的

int fgetc(FILE*stream)

他一次只能对一个字符进行读取,并且在一次读取之后文件指针将会指向下一个字符

#include

int main()
{
	//打开文件
	FILE* pf = fopen("bit.txt", "r");
	if (pf == NULL)
	{
		perror("fopen:");
		return -1;
	}
	// *** 作文件(读文件)
	int ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fgetc

我们对bit.txt中的字符进行一个一个的读取,打印出来了hello bit。



3、fputs
它的函数模型是这样子的

int fputs(const wchar_t* string,FILE* stream)

它的第一个参数为一个字符串,我们可以直接输入一行数据从内存到文本文件中去


#include
int main()
{
	//打开文件
	FILE* pf = fopen("bit.txt", "w");
	if (pf == NULL)
	{
		perror("fopen:");
		return -1;
	}
	// *** 作文件
	fputs("hello bit", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fputs

我们将hello bit写入到了bit.txt中
4、fgets
它的函数模型为这样子的

char* fgets(char* string,int n,FILE* stream)

第一个形参为读取的数据放到哪里的地址,第二个参数为读取多少个字节,

#include
int main()
{
	//打开文件
	FILE* pf = fopen("bit.txt", "r");
	if (pf == NULL)
	{
		perror("fpoen");
		return -1;
	}
	//读文件
	//读一行数据
	char arr[20] = { 0 };
	fgets(arr, 20, pf);
	printf("%s", arr);
	fgets(arr, 20, pf);
	printf("%s", arr);
	
	//关闭文件
	fclose(pf);
  pf = NULL;
	return 0;
}

fgets

调用两次fgets函数,将文本文件中的两行数据打印出来。



5、fprintf
它的函数模型是这样子的

int fprintf( FILE *stream, const char *format [, argument ]...);
#include
struct S
{
	int n;
	char m;
	double c;
};
int main()
{
	struct S s = { 10,'a',3.14 };

	FILE* pf = fopen("bit.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}

	fprintf(pf, "%d %c %lf", s.n, s.m, s.c);
	fclose(pf);
	pf = NULL;
	return 0;
}

fprintf

我们将s中的数据直接写入到了bit.txt中去
fscanf
它的函数模型是这样子的

int fscanf( FILE *stream, const char *format [, argument ]... );
#include
struct S
{
	int n;
	char m;
	double c;
};
int main()
{
	struct S s = { 0 };

	FILE* pf = fopen("bit.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}

	fscanf(pf, "%d %c %lf", &(s.n), &(s.m), &(s.c));
	printf("%d %c %lf", s.n, s.m, s.c);
	fclose(pf);
	pf = NULL;
	return 0;
}

fscanf

我们直接读到了bit.txt中的数据。



7、fwrite
它的函数模型为这样子的

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




#include
struct S
{
	int n;
	char m;
	double c;
};
int main()
{
	struct S s = { 10,'a',3.14 };

	FILE* pf = fopen("bit.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}

	fwrite(&s, sizeof(s), 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

fwrite

它在写入时候,是以二进制形式写进去的,因此我们打开文件查看时不能很好的编译出来,我们可以以二进制的形式查看。



fread
我们刚才将s中的数据以二进制的形式存储了进去
我们可以用fread函数将其查看
它的函数模型为

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
#include
struct S
{
	int n;
	char m;
	double c;
};
int main()
{
	struct S s = { 0 };

	FILE* pf = fopen("bit.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}

	fread(&s, sizeof(struct S), 1, pf);
	printf("%d %c %lf", s.n, s.m, s.c);
	fclose(pf);
	pf = NULL;
	return 0;
}

fread

函数的对比

函数名功能
scanf从标准输入流(键盘)读取格式化的数据
fscanf从所有的输入流读取格式化的数据
sscanf从字符中读取一个格式化的数据
printf把格式话的数据输出到标准输出(屏幕)上
fprintf把格式化的数据输出到所有输出流(屏幕/文件)上
sprintf把格式话的数据转化成对应的字符串
5、文件的随机读写

fseek
根据文件指针的位置和偏移量来定位文件指针
它的函数模型为

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

第二个形参为偏移量,第三个形参为起始位置,该函数是为了将文件指针移动到自己所需要的位置
假设一个文件中有 abcdef
当我们读取一次后,它的文件指针就指向了b的位置,我们可以用fseek函数将其的文件指针还原到起始位置

fseek(pf,-1,SEEK_CUR)

这样的话我们让文件指针指向了文件的起止位置
当我们

fseek(pf,3,SEEK_SET)

这样之后,那么文件指针便指向了d的位置。


偏移量的单位为字节,fseek函数的第三个形参为

那么此时又有一个问题,我们怎么知道现在文件指针现在指向哪里呢?
这里就有一个ftell函数,它的作用是求出文件指针当前的位置相对于起始位置的偏移量,它的函数模型为

long int ftell( FILE* stream );

它会直接返回一个long int 类型的数据我们就能看到当前文件指针相对于文件起始位置的偏移量
那么接下来我们介绍一个更简单的函数rewind,它的作用是让文件指针指向文件的起始位置,它的函数模型为

void rewind( FILE*stream)

它的返回值为空,非常的简单

6、文本文件和二进制文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。



数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。



如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。


以ASCII字符的形式存储的文件就是文本文件。



字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。



如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而
二进制形式输出,则在磁盘上只占4个字节(每种编译器会有所差异)。


7、文件读取结束的判定

feof
在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束,而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。



文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。


8、文件缓冲区

文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。


使用文件缓冲区可减少读取硬盘的次数。


文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。


通过 磁盘缓存 来实现,磁盘缓存本身并不是一种实际存在的存储介质,它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间, 来暂存从磁盘中读出 (或写入)的信息。


文件缓冲区的验证

#include 
#include 
int main()
{
 FILE*pf = fopen("bit.txt", "w");
 fputs("abcdef", pf);//先将代码放在输出缓冲区
 printf("睡眠10秒-已经写数据了,打开bit.txt文件,发现文件没有内容\n");
 Sleep(10000);
 printf("刷新缓冲区\n");
 fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
 printf("再睡眠10秒-此时,再次打开bit.txt文件,文件有内容了\n");
 Sleep(10000);
 fclose(pf);
 pf = NULL;
 return 0;
}

因为有缓冲区的存在,C语言在 *** 作文件的时候,需要做刷新缓冲区或者在文件 *** 作结束的时候关闭文件。


如果不做,可能导致读写文件的问题。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/584770.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存