C语言基础_文件操作-1

本文深入解析C语言中的文件操作,涵盖文件的打开与关闭、读写、定位等核心技能,通过实例演示如何使用fopen、fclose、getc、putc、fscanf、fprintf等函数,适合初学者及进阶学习者。
摘要由CSDN通过智能技术生成

目录

写在前面

学习目标:

学习总结:

正文:

1- 文件打开、关闭

2- getc、putc

3- fprintf 、fscanf (gets不安全,用fgets)

4- fgets、gputs

5- 文件光标操作

a- fseek、ftell(long范围内操作,常操作二进制文件!)

6-  fread fwrite

7- 其他(了解)



写在前面

之前从单片机起步,没用过文件操作,甚至看C语言的书,感觉文件操作好高深,对其望而生畏。直到接触到linux,尤其是学习了字符设备驱动,发现字符设备就是对文件的操作。都说linux下一切皆文件,对其理解还不是太深,那就满满学习吧。

学习目标:

掌握C语言中的文件操作,包括:打开、关闭;读写;定位等。

 

学习总结:

1- 文件打开、关闭 fopen;fclose,及其返回值。fopen正常返回文件指针,异常返回NULL;fclose正常返回0,异常返回EOF。注意以w/w+模式打开文件,会冲掉文件原有内容,另外打开模式用双引号,不是单引号!!

FILE * fp = fopen("文件", "mode");
fclose(fp);

 

2- getc、putc。getc注意循环输入吃掉回车,注意putc不自动添加回车

循环输入条件:while( (ch = getc(stdin)) != '\n')

ch = getc(fp);
putc(ch,fp);

 

3- fprintf、fscanf用法和printf、scanf相似。用于向文件(显示器)写东西、从文件(键盘)读出数据。注意gets、puts用法不安全,用fputs、fgets代替

fprintf(fd, "%s", buff);
fscanf(fd, "%s", buff);

 

4- fgets、fputs用于从文件(stdin)读数据、输出到stdout。fgets比gets安全,不要用gets,因为fgets完全可以替代gets。

fgets(buff, len, fp);
fputs(buff, fp);

注意事项:

fgets会接收回车'\n',且当输入小于len,会在输入后面加上字符串结尾'\0',并且如果加上'\0'后还没到len,会加上换行符'\n'。

如果超过了len,则最多接受len-1个字符,因为还要加上'\0',这时候就没法在'\0'后面加'\n'了。也就是fputs没有换行的效果了。

所以为了有换行效果,输入最大len-2个字符。循环输入另说。

 

循环输入条件:while( (fgets(buf,10,stdin) != NULL ) && (buf[0] != '\n'))

如果输入超过len个字符仍会打印,并且有换行效果。是由于fgets一次最多读入len-1个字符,并在其结尾加入'\0'(不加'\n'!!!),并将其放到输出缓存中,当读完所有一行数据一块输出

 

使用fgets输入文件名时,文件名中包含'\n',需要处理掉,不然会影响strcmp比较,这里涉及到strcmp、strchr字符串知识点。

 

用法:

a-fgets从stdin/文件1 读入数据

b- fprintf将数据写入文件2

c- fscanf从文件2读出数据

d- fputs将数据刷新到屏幕

 

5- 了解文件指针偏移设置。fseek、ftell;fsetpos、fgetpos。如果使用后者操作偏移量使用fpos_t.__pos=偏移量

fread;fwrite;fseek;ftell;fgetpos;fsetpos;fflush等

 

6-  fread fwrite重点

/*返回读出的个数。将读入的数据写到buf中
示例:
以char为单位,读15个char数据
size_t cnt = fread(buf,sizeof(char),15,fp);
*/
size_t fread(buf,读单位,读多少个,FILE *fp);


/*写,将buf中数据写到fp中*/
size_t fwrite(buf,写单位,写多少个,FILE *fp);

 

 

rewind将文件指针置为开头

rewind(fp);

exit和return区别

 

 

正文:

1- 文件打开、关闭

fopen、fclose对应于文件打开关闭,

语法:

fopen(文件名,打开模式);//打开成功返回文件指针FILE *; 打开失败返回NULL
fclose(文件句柄);//文件关闭成功返回0; 关闭失败返回EOF

在这里设计到文件指针的概念,C语言中该变量为FILE *数据类型,我们对于文件的操作都是通过文件指针进行的。对其暂时不要过多探究,只需知道我们操作文件必须借助文件指针即可。

介绍三个便文件指针:

stdin标准输入,比如从键盘读取
stdout标准输出,比如输出到显示器
stderr标准错误输出,比如输出到显示器

对于文本文件打开模式主要有r、w、a三种,衍生出r+、w+、a+。对于二进制文件在此基础上加上b参数即可。对于文本文件和二进制文件区别,暂时不纠结

注意打开模式、文件名用双引号,双引号!!!!

r打开一个已有的文本文件,允许读取文件。
w打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。简而言之就是,文件不存在创建,不论文件里有没有数据我都清空,重写。
a打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+打开一个文本文件,允许读写文件。
w+打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。
rb; wb; ab; ab+; a+b; wb+;w+b; ab+;a+b这里的几种模式是针对二进制模式文件操作的模式

先上个小例子来看下,这两个函数怎么用,简单地打开一个文件,然后关闭。通过下面的例子,对文件的基本操作应该有了基本的认识了吧,let's go,继续往下搞。

解读写exit()函数,表示退出程序,和return不同。加入我在递归调用中调用exit函数,程序会终止整个程序;如果使用return,只会终止本次调用。

#include<stdio.h>
#include<stdlib.h>  //exit()

int main()
{
	FILE *fp;
		
	fp = fopen("tmp.txt","r");
	if(NULL == fp)
	{
		printf("open file fail\n");
		exit(1);
	}
	printf("open file succ\n");	
	
	if(EOF == fclose(fp))
	{
		printf("close file fail\n");
		exit(1);
	}		
	printf("close file succ\n");	
	
	return 0;
}

 

2- getc、putc

和getchar、putchar类似,好像这两个用的不多,了解即可。一次只能接受一个字符

语法:

接收读入的字符 = getc(文件指针);  //从文件指针读出1个字符
putc(文件指针); //向文件指针写一个字符

举例:

A-  从键盘输入一个字符,并将其输出到屏幕。

注意:

I- getc()循环入口处理回车。getc()一次只能读一个字符,假如我们输入a[回车],程序会先将a读入,打印,第二次读入回车,如果不在while中加入getc吃掉回车,下次在while循环入口检测到回车就会停止循环!

II-  putc()不自动加回车。

#include<stdio.h>
#include<stdlib.h>  //exit()

int main()
{
	char ch;
	printf("please enter:");
	while( (ch = getc(stdin)) != '\n')
	{
		printf("printf char:");
		putc(ch,stdout);
		getc(stdin); //吃掉回车
		printf("\n");
		printf("please enter:");
	}
	
	return 0;
}

 

B- 从文件读并输出

注意:应该先读一个字符看是否是文件结尾EOF。另外文件打开模式不可以用w/w+,因为会冲掉文件原有内容。

#include<stdio.h>
#include<stdlib.h>  //exit()

int main()
{
	FILE * fp;
	char ch;
	fp = fopen("tmp.txt","a+");
	if(fp == NULL)
	{
		printf("open file fail\n");
		exit(1);
	}
		
	while( (ch = getc(fp)) != EOF )
	{
		putc(ch, stdout);		
	}
	
	if(EOF == fclose(fp))
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

C- 文件备份

打开一个文件,将其内容复制到另一个文件,另一个文件名在第一个文件名基础上加上_bk。

写完这个程序,有木有感觉和前面的不一样,文件操作好像是那么回事了。好像抓住了文件操作的命脉,有木有。

哈哈哈哈,这样就是个简易的文件备份程序。将它复杂话就是真正的文件备份了。

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE * fp_src,*fp_bk;
	char ch;
	char name[10];
	
	/*打开源文件,打开模式可以用r,因为我是边调试边写的,所以用了a+*/
	fp_src = fopen("tmp","a+");
	if(fp_src == NULL)
	{
		printf("open src_file fail\n");
		exit(1);
	}
	
	/*构造备份文件名. 10-4是追加_bk,并余留一个字符*/
	strncpy(name,"tmp",10-4);
	strcat(name,"_bk");
	//puts(name);
	
	/*打开备份文件*/
	fp_bk = fopen(name,"w");	
	if(fp_bk == NULL)
	{
		printf("open bk_file fail\n");
		exit(1);
	}
	
	/*将源文件数据拷贝到备份文件*/
	while( (ch = getc(fp_src)) != EOF )
	{
		putc(ch,fp_bk);		
	}
	
	if((EOF == fclose(fp_src)) || (EOF == fclose(fp_bk)))
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

3- fprintf 、fscanf (gets不安全,用fgets

用法同printf、scanf,一个是操作屏幕、键盘,另一个是操作文件,当然fprintf、fscanf也可以操作屏幕、键盘

fprintf写入正确,返回值为写入的个数,否则返回负值。

fscanf读入成功,返回读出的个数;如果到达文件结尾或读错误,返回EOF

语法:

/*printf将数据写到屏幕,fprintf将数据写到文件*/
fprintf(待写入文件指针,格式字符串);

/*scanf从键盘读出数据,fscanf从文件读出数据*/
fscanf(读出数据的文件指针,格式字符串);

 

示例:

功能:从键盘读入字符串输入到文件,并打印出来

注意使用gets,在ubuntu环境下会有编译警告,查了下其他博客资料,说是gets不安全最好用fgets代替。

参考:https://blog.csdn.net/weixin_34336526/article/details/89696487

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE * fp;
	char words[100];
	
	/*打开文件*/
	fp = fopen("tmp","w+");
	if( NULL == fp)
	{
		fprintf(stdout,"open file fail\n");
		exit(1);
	}
	printf("now begain to write file\n");
	printf("please enter:\n");
	
	/*将输入的字符串写入文件
	*循环入口条件,输入非空,gets丢弃回车符'\n'
	*/
	while( (gets(words) != NULL ) && (words[0] != '\0'))
	{
		fprintf(fp,"%s", words);		
	}
	fprintf(fp,"\n");
	
	/*将文件指针定位到开头*/
	rewind(fp);
	
	/*读出文件*/
	while(fscanf(fp,"%s",words) != EOF)
		puts(words);
	
	if( EOF == fclose(fp) )
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

4- fgets、gputs

用法和gets、puts安全,为何安全先不管。。。

语法:

/*文件指针可以是stdin*/
fgets(获取数据的文件指针, 最大获取的字符个数, 输出字符的容器);

/*文件指针可以是stdout*/
fputs(写入的数据 ,写入数据的文件指针);

需要注意的是:

a- fgets获取字符时,遇到回车符不丢弃;gets丢弃回车符'\n',所以3-、4-的循环入口判断条件分别为'\0'和'\n'

b- fputs打印时候不打印回车;puts打印回车。

 

示例:

A- 将3-示例中gets、puts用fgets、fputs代替

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE * fp;
	char words[100];
	
	/*打开文件*/
	fp = fopen("tmp","w+");
	if( NULL == fp)
	{
		fprintf(stdout,"open file fail\n");
		exit(1);
	}
	printf("now begain to write file\n");
	printf("please enter:\n");
	
	/*将输入的字符串写入文件
	*循环入口条件,输入非空
	*/
	//while( (gets(words) != NULL ) && (words[0] != '\0'))
	while((fgets(words,100,stdin) != NULL) && (words[0] != '\n'))	
	{
		fprintf(fp,"%s", words);		
	}
	fprintf(fp,"\n");
	
	/*将文件指针定位到开头*/
	rewind(fp);
	
	/*读出文件*/
	while(fscanf(fp,"%s",words) != EOF)
	{
		fputs(words,stdout);
		fputs("\n",stdout);
	}
	
	/*关闭文件*/
	if( EOF == fclose(fp) )
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

B- fgets、fputs注意说明

知识点:

fgets会接收回车'\n',且当输入小于len,会在输入后面加上字符串结尾'\0',并且如果加上'\0'后还没到len,会加上换行符'\n'。

如果超过了len,则最多接受len-1个字符,因为还要加上'\0',这时候就没法在'\0'后面加'\n'了。也就是fputs没有换行的效果了。

所以为了有换行效果,输入最大len-2个字符。循环输入另说。

I- 可以用下面代码做实验:

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	char buf[10];

    /*输入小于等于8个有换行效果,否则无换行效果*/
	fgets(buf,10,stdin);
	fputs(buf,stdout);

	return 0;
}

II- 循环输入

注意:此时如果输入超过10个字符仍会打印,并且有换行效果。是由于fgets一次最多读入9个字符,并在其结尾加入'\0'(不加'\n'!!!),并将其放到输出缓存中,当读完所有一行数据一块输出

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	char buf[10];
	
	while( (fgets(buf,10,stdin) != NULL ) && (buf[0] != '\n'))
		fputs(buf,stdout);

	return 0;
}

 

5- 文件光标操作

包括fseek、ftell;fsetpos、fgetpos

 

a- fseek、ftell(long范围内操作,常操作二进制文件!)

fseek用于将文件指针定位到文件某个位置

ftell用于返回文件指针位置到文件开头的距离

二者配合使用,用于在long数据范围内操作文件

语法:

/*偏移量为long型,常传入0L、1L、-1L等数据*/
fseek(fp, 偏移, 模式);

/*返回的距离为long型*/
距离 = ftell(fp);

fseek的偏移模式有:

SEEK_SET文件开始位置
SEEK_CUR文件当前位置
SEEK_END文件结尾位置

 

形如:fseek表示将文件指针偏移到文件末尾,ftell返回文件当前位置到文件开头位置的距离

fseek(fp, 0L, SEEK_END);
dist = ftell(fp);

示例:

打开一个文件,以文件末尾向文件开头的方向,将字符输出。

这里操作的二进制文件,ab+

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE * fp;
	char ch;
	long count,last;
	
	/*打开文件*/
	fp = fopen("tmp","ab+");
	if( NULL == fp)
	{
		fprintf(stdout,"open file fail\n");
		exit(1);
	}
	
	/*定位到文件结尾*/
	fseek(fp, 0L, SEEK_END);
	
	/*计算文件开头到结尾的距离*/
	last = ftell(fp);
	
	for(count=1L; count <= last; count++)
	{
		fseek(fp, -count,SEEK_END);//由于文件最后一个为EOF,所以先移到倒数第1个字符
		ch = getc(fp);
		
		if(ch != '\n')
			putc(ch,stdout);
	}
	printf("\n");
	
	
	
	/*关闭文件*/
	if( EOF == fclose(fp) )
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

b- fsetpos、fgetpos(fpos_t范围内操作

fpos_t为新的数据类型

语法

/*设置文件指针位置*/
fsetpos(fp, fpos_t *pos);

/*获取文件指针位置*/
fgetpos(fp, fpos_t *pos);

 

示例:

打开一个文件,输入字符,然后文件头将字符输出

操作文件偏移可以用如下赋值,注意是两个下划线

pos.__pos = 偏移量;
#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE * fp;	
	char buf[10];
	fpos_t pos;

	
	/*打开文件*/
	fp = fopen("tmp","wb+");
	if( NULL == fp)
	{
		fprintf(stdout,"open file fail\n");
		exit(1);
	}
	/*暂存文件开头位置*/
	fgetpos(fp,&pos);
	
	while( (fgets(buf,10,stdin) != NULL) && (buf[0] != '\n') )
		fprintf(fp,"%s",buf);
	
	//pos.__pos = 5;
	fsetpos(fp, &pos);
	while( fscanf(fp,"%s",buf) != EOF)
		fputs(buf,stdout);	
	
	
	/*关闭文件*/
	if( EOF == fclose(fp) )
	{
		printf("close file fail\n");
		exit(1);
	}
		
	return 0;
}

 

6-  fread fwrite

语法:

/*返回读出的个数。将读入的数据写到buf中
示例:
以char为单位,读15个char数据
size_t cnt = fread(buf,sizeof(char),15,fp);
*/
size_t fread(buf,读单位,读多少个,FILE *fp);


/*写,将buf中数据写到fp中*/
size_t fwrite(buf,写单位,写多少个,FILE *fp);

示例:

从源文件中读数据,将其追加写到tmp_dest文件中。

特别注意:使用fgets输入文件名时,文件名中包含'\n',需要处理掉,不然会影响strcmp比较,这里涉及到strcmp、strchr字符串知识点。

这里使用了setvbuf()、feof()、ferror(),详细解读见其他。

#include<stdio.h>
#include<stdlib.h>  //exit()
#include<string.h> //字符串头文件

int main()
{
	FILE *fp_dest,*fp_src;
	int ret;
	char *pos;
	char buf_r[1024];
	char buf_w[1024];
	char name_src[10];
	char tmp[1024];
	size_t cnt;
	
	/*初始化缓存,注意是单引号。。。*/
	memset(buf_r,'\0',sizeof(buf_r));
	memset(buf_w,'\0',sizeof(buf_w));
	
	/*打开待写入文件fp_dest,并设置缓存为buf*/
	fp_dest = fopen("tmp_dest","a");
	if(NULL == fp_dest)
	{
		fprintf(stderr,"open file:%s fail\n","tmp_dest");
		exit(1);
	}
	ret = setvbuf(fp_dest,buf_r,_IOFBF,1024);
	if(ret != 0)
	{
		fprintf(stderr,"setvbuf err\n");
		exit(1);
	}
	
	/*开始操作*/
	fputs("enter name_src of to be read:",stdout);	
	while((fgets(name_src,10,stdin) != NULL) && (name_src[0] != '\n') )
	{
		/*处理掉fgets末尾的‘\n'*/
		pos = strchr(name_src,'\n');
		if(pos)
			*pos = '\0';
		
		/*如果文件名一样,不做操作,进行下一次循环*/
		if(strcmp(name_src,"tmp_dest") == 0)
		{
			fprintf(stderr,"dest_file:%s is same as src_file:%s \n","tmp_dest",name_src);
		}
		else if((fp_src=fopen(name_src,"r")) == NULL) //打开文件失败
		{
			fprintf(stderr,"open file:%s fail\n",name_src);
		}
		else //开始文件读写
		{
			/*设置输出缓存*/
			ret = setvbuf(fp_src,buf_w,_IOFBF,1024);
			if(ret != 0)
			{
				fprintf(stderr,"setvbuf err\n");
				exit(1);
			}
		
			/*将fp_src中内容追加到fp_dest*/
			while((cnt = fread(tmp,sizeof(char),1024,fp_src)) > 0 ) 
				fwrite(tmp,sizeof(char),cnt,fp_dest);
				
			
			/*校验读写是否有误*/
			if(ferror(fp_dest) != 0)
				fprintf(stderr,"write fail\n");
			if(ferror(fp_src) != 0)
				fprintf(stderr,"read fail\n");
			
			fclose(fp_src);
			fputs("enter next file name to be read:",stdout);		
		}
	}	

	/*关闭文件*/	
	if(EOF == fclose(fp_dest))
	{
		fprintf(stderr,"close file:%s fail","tmp_dest");
		exit(1);
	}

	return 0;
}

 

7- 其他(了解)

a- setvbuf

设置缓存。比如我们输出到屏幕时,会将其先输出到缓存,然后才输出到屏幕,这里为文件指针指定缓存。但实际作用还没感受到,了解。

/*这里模式主要由
_IOFBF:满缓存
_IOLBF:行缓存
_IONBF:无缓存*/
setvbuf( 需要缓存的文件指针,缓存容器, 模式,大小);

参考:https://www.runoob.com/cprogramming/c-function-setvbuf.html

 

b- fflush

将未输出的缓存强制输出

fflush(FILE *fp);

printf为行缓存,有‘\n’强制输出。如果不加换行,可使用fflush(stdout)强制输出

有些场景需要强制刷新缓存:https://blog.csdn.net/pfl_327/article/details/78903110

 

c- feof

检查是否到文件结尾,到结尾返回非0值

int feof(FILE *fp);

d- ferror

检查读写是否有误,有误返回非0值

int feof(FILE *fp);

 

e- ungetc

将字符放回到输入流。

int ungetc(int c, FILE *fp);

比如用于文件中字符替换。打开一个文件,读到是'a'则将其替换为'b',否则不替换。

参考:https://www.runoob.com/cprogramming/c-function-ungetc.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值