Linux系统编程学习--第2天标准IO缓冲区文件操作

什么是缓冲区?

一块内存区,在输入输出设备和CPU间,用来缓存数据。

比如键盘输入给cpu信息,我输一个字符告诉内核一次,太占用资源,那么就用缓冲区存起来你输入的信息,当达到一定条件时,再上传内核。
在这里插入图片描述
这三个stdin、stdout、stderr是缓冲区的句柄:

STDIN 是控制台输入缓冲区的句柄,而 STDOUT 和 STDERR 是控制台活动屏幕缓冲区的句柄
那么什么句柄?
答:句柄是硬件资源指针的引用,一种特殊的指针。
他和指针有什么区别?
答:比如声卡,一堆进程要利用声卡发出声音,那不能都给他们分配一个资源指针,这时候利用句柄实际上也就是一个int值,引用了同一个硬件资源指针,句柄在不同进程中是不一样的,比如在第一个进程中句柄可能为1,在第二个进程中句柄可能为2.

此外缓冲区还有三种缓冲模式,分别是行缓冲、块缓冲、不缓冲。
行缓冲:遇到换行符\n或者缓冲区存满后上交内核层
块缓冲:存满后上交内核层。
不缓冲:来一个字符上交一次内核层。
STDIN和STDOUT就是行缓冲模式,STDERR就是不缓冲模式。

来看几个关于缓冲区的函数
1、清理缓冲区的函数
fflush();
里面的参数填写缓冲区句柄,比如你要清理标准输入流就调用:fflush(stdin);标准输出流同样。
2、FILE指针
在这里插入图片描述
3、缓冲区文件打开方法fopen
FILE * fopen(const char * path,const char * mode);

返回值成功返回操作文件的指针,失败返回NULL
参数path:你要打开文件的路径
参数mode:打开方式参考下图

在这里插入图片描述
4、缓冲区关闭文件方法fclose
在这里插入图片描述

演示

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","r");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

在这里插入图片描述
5、fputc

函数原型:int fputc(int c, FILE *stream)
函数功能:写一个字符到文件中
函数参数:要写入的字符,要写入的文件句柄
函数返回值:成功:字符c,失败: EOF

演示
代码

#include <stdio.h>
int main(int argc,char* args[])
{
//注意!!!!你要往文件中写入内容的话,打开文件的方式必须是可写的,如果是w、w+的话在写入之前会清空文件的内容,a或a+的话则是追加方式。
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		if(fputc('s',fp)!=EOF)
		{
			printf("写入字符成功\n");
		}else
		{
			printf("写入字符失败\n");
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

文件原内容
在这里插入图片描述
运行完后
在这里插入图片描述
6、fgetc

函数原型:int fgetc(FILE *stream);
函数功能:从文件中读取一个字符
函数参数:要读取的文件句柄
函数返回值:成功:字符c,失败: EOF

演示
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		int readchar;
		if((readchar=fgetc(fp))!=EOF)
		{
			printf("读取字符成功\n");
			printf("读取到的字符为:%c\n",readchar);
		}else
		{
			printf("读取字符失败\n");
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

文件内容:
在这里插入图片描述
结果
在这里插入图片描述

6、feof

函数原型:int feof(FILE *stream);
函数功能:判断文件是否读到末尾
函数参数:文件句柄
函数返回值:文件未结束: 0,文件已结束: 1

演示
我们可以结合fgetc读取文件的所有字符
代码:

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		int readchar;
		while(feof(fp)!=1)
		{
			if((readchar=fgetc(fp))!=EOF)
			{
				printf("%c",readchar);
			}
		}
		printf("\n");
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象:
在这里插入图片描述
下面介绍字符串读写:
7、fgets

函数原型:char * fgets(char * s, int size, FILE * fp);
函数功能:从fp指向的文件中读出一行(最多size-1个字符) 直到出现换行字符、读到文件尾或是已读了size-1 个字符为止,写入s指向的缓冲区。
函数参数:s  保存读取到的字符、size  要读取的字符的个数、fp  为文件流指针
函数返回值:成功返回字符串指针,失败返回NULL

演示
演示中的错误
在这里插入图片描述
fgets的函数参数中有字符缓冲区、读取多少字符、文件句柄。那么我们定义一个char*类型的变量,为防止它变为野指针,让他指向NULL,相当于给气球拴根绳。编译运行上述代码现象如下
在这里插入图片描述
可以看到段错误,为什么会出现这个错误?
因为函数的功能是给你这个指针的空间中塞入读取到的字符,然而你这个指针指向的空间是NULL所以出现段错误。
如何解决?
可以给指针分配空间,或者改用定义字符数组的方式。

代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		char ss[20];
		if(fgets(ss,5,fp)!=NULL)
		{
			printf("读取成功\n");
			printf("读取到的字符串:%s\n",ss);
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述

8、fputs

函数原型:int fputs(const char *s, FILE *fp)
函数功能:将字符串s写入fp指向的文件中
函数参数:s:   要写入文件的缓冲区指针、fp:要写入的目标文件的流指针
函数返回值:该函数返回一个非负值,如果发生错误则返回 EOF

演示
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		char ss[20]="hello world!!!!";
		if(fputs(ss,fp)!=EOF)
		{
			printf("写入成功\n");
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述
9、块读函数fread

函数原型:   size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp);
函数功能:以块的形式读入定义好的数据缓冲区
函数参数:
ptr: 要读入的缓冲区指针, 提前申请好的
size: 要读出的信息单元的大小
nmemb:要读出的信息单元的个数, size* nmemb不大于ptr大小
fp:   要读的文件指针, 提前用fopen打开的
函数返回值:成功返回读取的信息单元的个数,失败: EOF

在我之前的老师讲这个方法时只讲了一种很局限的用法,就是size给1、一个字节一个字节的读取,读取nmemb个。
那这就体现不到这个方法为什么叫块读函数,块读就是一块一块的读,将几个字节合成一块读出来存储到一块,一共读nmemb个块。
接下来我是用一种能现块读的方法演示

首先文件内容有这些
在这里插入图片描述
然后我定义一个short数组来接收读到的数据,因为short大小有两个字节所以在读时,参数size(块的大小给2个字节),一共读5个,然后读完之后看一下第一个数据
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		short ss[5];
		if(fread(ss,2,5,fp)!=EOF)
		{
			printf("读取成功\n");
			printf("%d\n",ss[0]);
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述
可以看到读到了一个较大的数字12081
为什么会是这个数字?
12081(十进制)->0010 1111 0011 0001(二进制)
文件的头两个字符为1、/
1在ascll表中对应49
/在ascll表中对应47
49(十进制)->0011 0001(二进制)
47(十进制)->0010 1111(二进制)
先把1读到低八位,再把/读到高八位。第一个块读取完毕,所以就得到了12081.
10、块写函数fwrite

函数原型:      size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *fp);
函数功能:以块的形式写入文件
函数参数:
ptr: 要写入的缓冲区指针, 提前申请好的
size: 要写入的信息单元的大小 一般情况下, size设为1
nmemb:要写入的信息单元的个数, size* nmemb不大于ptr大小
fp:   要写的文件指针, 提前用fopen打开的
函数返回值:成功返回读取的信息单元的个数,失败: EOF

直接在之前的基础上再次写入文件
演示
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		printf("打开文件成功\n");
		short ss[5];
		if(fread(ss,2,5,fp)!=EOF)
		{
			printf("读取成功\n");
			printf("%d\n",ss[0]);
		}
		if(fwrite(ss,2,5,fp)!=EOF)
		{
			printf("写入成功\n");
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述
可以看到在之后多加了是个字符,刚好是5个short的大小。
11、格式化写函数fscanf

函数原型:int fscanf ( FILE * fp, const char * format, ... );
函数功能:fscanf从fp中格式化输入
函数参数:
fp: 文件句柄
format:格式
函数返回值:成功返回读取的信息单元的个数,失败: EOF

文件内容
在这里插入图片描述
我要把AAAA,BBBB,CCCC分别读到三个字符串
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		char ss0[20],ss1[20],ss2[20];
		printf("打开文件成功\n");
		if(fscanf(fp,"%s %s %s",ss0,ss1,ss2)!=EOF)
		{
			printf("读到的字符串:%s,%s,%s\n",ss0,ss1,ss2);
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述
11、格式化读函数fprintf

函数原型:int fprintf(FILE *fp, const  char *format, ...)
函数功能:把数据按格式存入文件
函数参数:
fp: 文件句柄
format:格式
函数返回值:成功返回读取的信息单元的个数,失败: EOF

把姓名、年龄按照“姓名:xxx,年龄:xx。”的格式存入文件
演示
代码

#include <stdio.h>
int main(int argc,char* args[])
{
	FILE *fp=fopen("/home/xxx/Desktop/shell/1234","a+");
	if(fp!=NULL)
	{
		char name[20];
		int age;
		printf("打开文件成功\n");
		printf("请输入你的姓名\n");
		scanf("%s",name);
		printf("请输入你的年龄\n");
		scanf("%d",&age);
		if(fprintf(fp,"姓名:%s,年龄:%d.",name,age)!=EOF)
		{
			printf("存储信息成功\n");
		}
	}
	if(fclose(fp)==0)
	{
		printf("关闭文件成功\n");
	}
}

现象
在这里插入图片描述
12、文件指针定位函数
当读取文件时,需要打开,打开后文件指针在开头,就像你打开一个txt文件后的光标在开头
在这里插入图片描述
读取字符光标就会往后移动
在这里插入图片描述
假如你读取完1后光标这时候过去了,要想再读到这个字符,再打开不方便,那么就用到一些光标移动函数。
在这里插入图片描述
fseek函数

函数原型:int fseek(FILE *stream, long int offset, int whence)
函数功能:将指针定位到指定位置
函数参数:stream文件句柄,offset相对于whence的偏移量,whence:有文件开始SEEK_SET、当前位置SEEK_CUP、文件结尾SEEK_END。
函数返回值:如果成功,则该函数返回零,否则返回非零值。

ftell函数
告诉你文件指针距离开头有几个字节,通常配合fseek函数求文件大小。
fseek(fp, 0, SEEK_END);
len = ftell(fp);

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值