9.2 Linux_I/O_标准I/O相关函数

打开与关闭

文件打开就是判断这个文件资源可不可以被占用,如果可以,就能够打开成功,否则打开失败

文件关闭就是释放文件资源

1、打开文件

1.1 函数声明

FILE *fopen(const char *pathname, const char *mode);

返回值:出错返回NULL,成功返回流指针

pathname:文件的路径。如果是当前目录下的文件,只写文件名即可。其他目录需要写绝对路径

mode:打开的模式

模式含义适用情况
r     rb只读,文件必须存在只想查看,不想修改文件
w    wb只写,先清空原文件,写时覆盖原文件,没有则创建只写一次的情况
a     ab只写,追加原文件,没有则创建需要多次写入的情况
r+    r+b读写,写时覆盖原文件,文件必须存在可使用光标随意位置写入
w+  w+b读写,先清空原文件,写时覆盖原文件,没有则创建可使用光标随意位置写入
a+   a+b读写,写时追加原文件,没有则创建不可指定光标位置

注意:打开文件后,必须判断返回值是否有效。只有当返回值有效时,才能对文件进行操作。

注意:打开文件后,光标指向文件的开头 

1.2 基本使用方法

使用步骤:

  1. 定义一个流指针fp
  2. 调用fopen打开文件
  3. 判断返回值有效性

具体代码实现如下:

#include <stdio.h>

int main(){
	//1.定义一个流指针
	FILE* fp = NULL;
	//2.打开文件
	fp = fopen("./test.c","r");//以只读方式打开文件./test.c
	//3.判断返回值有效性
	if(fp == NULL){
		perror("fopen");//打印错误信息
		return -1;
	}
	printf("open success\n");
    fclose(fp);
	return 0;
}

代码执行结果如下: 

2、关闭文件

2.1 函数声明如下:

int fclose(FILE *stream);

返回值:成功返回0;失败返回EOF(-1),并设置错误号errno

stream:指向要关闭的文件的流指针

2.2 关闭文件函数所做的事情:

流关闭时,自动刷新缓冲区中的数据并释放缓冲区,这意味着将之前对文件的写入内容会真正的写入到磁盘中,并释放内存开辟的缓冲区。

如果不调用fclose但是程序结束,也会有fclose的同样的效果。

3、打印错误信息

3.1 函数声明如下:

//先输出字符串s,再输出错误号对应的信息
void perror(const char *s);

//根据错误号errnum,返回对应的错误信息
char *strerror(int errnum);

对于strerror的参数errnum,固定填入errno即可。这需要先包含#include <errno.h>

3.2 基本使用方法

具体代码实现如下: 

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	fp = fopen("./alksjdla","r");
	if(fp == NULL){
		perror("perror");                        //perror 打印错误
		printf("strerror:%s\n",strerror(errno)); //strerror 打印错误
		return -1;
	}
	printf("open success\n");
	return 0;
}

代码执行结果如下: 

读取与写入

输入输出两大问题:

  • 光标问题:

读取与写入操作都是从光标位置开始执行,执行完成后都会对光标位置进行修改。因此读写操作首先要考虑的时当前光标位置,以及执行之后光标移动的结果

  • 缓冲区问题:

输出(写入)操作,都是行缓冲模式,如果输出结果没有'\n',那么需要在输出之后使用fflush手动刷新缓冲区,将缓冲区内容写入到文件中。

1、按字符输入输出

1.1 按字符输入函数声明如下:

//从文件中读取一个字符
int fgetc(FILE *stream);

//这是fgetc的宏,不是函数
int getc(FILE *stream);

//只能读取标准输入的一个字符,等同于fgetc(stdin)
int getchar(void);

返回值:成功返回字符,失败或读到文件末尾返回EOF(-1) 

stream:指向要读取的文件的流指针

注意:从文件的当前光标位置读取一个字符,读取后光标偏移一个字符 

1.2 按字符输出函数声明如下:

//向文件中写入一个字符
//同样,fputc只是将数据存入缓冲区,当遇到\n时才真正的存入到文件中
int fputc(int c, FILE *stream);

//这是fputc的宏,不是函数
int putc(int c, FILE *stream);

//只能向标准输出上输出一个字符,等同于fputc(stdout)
//同样,putchar只是将数据存入缓冲区,当遇到\n时才打印到屏幕
int putchar(int c);

返回值:成功返回写入的字符,失败返回EOF

c:要写入的字符

stream:指向要写入的文件的流指针

注意:将光标移动(与模式有关),在光标位置写入一个字符,写入后光标偏移一个字符  

1.3 基本使用方法

具体代码实现如下:  

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	//1.打开文件
	fp = fopen("./file","a+");//以追加方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
	//读取文件
	printf("1:输出%%c %c\n",fgetc(fp));//光标指向0,读取后偏移一个字符
	printf("2:输出%%c %c\n",fgetc(fp));//光标指向1,读取后偏移一个字符
	//写入文件
	fputc('x',fp);//先将光标指向末尾,再向光标位置写入一个字符,写后光标偏移一个字符
	//读取文件
	printf("3:输出%%d %d\n",fgetc(fp));//光标指向末尾,没有数据
	//关闭文件
	fclose(fp);
	return 0;
}

 代码执行结果如下: 

2、按行输入输出

2.1 按行输入

2.1.1 按行输入函数声明如下:

//从文件中读取数据,存放在s中
char *fgets(char *s, int size, FILE *stream);

//从标准输入上读取数据,存放在s中
char *gets(char *s);  //不使用,因为没有指定读取数据的大小,容易造成缓冲区溢出

返回值:成功返回缓冲区首地址,失败返回NULL

s:将读取的内容放置的缓冲区地址

size:每次读取的字符的个数,'\0'同样算一个字符。size>输入字符个数时,会将回车也存入s

          比如size = 3,输入abcd,最终缓冲区存放的数据为ab'\0',而不是abc

          比如size = 10,输入abcd,最终缓冲区存放的数据为abcd '\n' '\0'

stream:从哪一个文件读取数据

注意:fgets执行后,光标会进行移动

2.1.2 size验证代码如下:

  • 当size>字符个数时,会将\n存入缓冲区 

  • 当size<字符个数时,'\0'也算一个字符

2.1.3  按行输入的原理验证

按行输入函数 fgets()的原理就是读到回车就算一行,假如这一行有n个字符,如果n<size,那么读取到的是完整的一行数据;如果n>=size,那么本次只读取size-1个字符,其余字符下次调用fgets()会接着读取。

具体代码如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	char buf[100];
	FILE* fp = NULL;
	//打开文件
	if((fp = fopen("./file","r")) == NULL){
		perror("fopen");
		return -1;
	}
	//读取文件
	fgets(buf,5,fp);//从fp文件上一次最多读取5个字符
	printf("1:%s\n",buf);
	fgets(buf,5,fp);//从fp文件上一次最多读取5个字符
	printf("2:%s\n",buf);
	fgets(buf,5,fp);//从fp文件上一次最多读取5个字符
	printf("3:%s\n",buf);
	
	//关闭文件
	fclose(fp);
	return 0;
}

代码执行结果如下: 

2.1.4 应用:获取文件的行号 

具体代码实现如下:

int main(){

	FILE* fp = NULL;
	int i = 0;
	char buf[3] = {0};//数组大小可随意填,后面有判断是否换行的处理,不需要一次读完一行

	if((fp = fopen("./file","a+")) == NULL){//以追加方式,可读可写
		perror("fopen");
		return -1;
	}
    //计算文件有多少行
	while(fgets(buf,sizeof(buf),fp) != NULL){
		if(buf[strlen(buf)-1] == '\n'){//判断倒数第二个字符是否为换行,是换行代表这行读完
			i++;
		}
	}
	fclose(fp);
	return 0;
}

2.2 按行输出 

2.2.1 按行输出函数声明如下:

//将字符串数据s输出到文件中
int fputs(const char *s, FILE *stream);

//将字符串数据s输出到标准输出上
int puts(const char *s);

返回值:成功返回非负数,失败返回EOF

s:要输出的字符串

stream:指向要输出到的文件位置

注意:puts会自动添加'\n',fputs不会自动添加'\n'

注意:fputs执行后,光标会进行移动

2.2.2 基本使用方法

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	//1.打开文件
	fp = fopen("./file","a+");//以追加方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}

    //这里的fputs能写进去是因为文件关闭了,应该之后用fflush刷新一下
	fputs("fputs",fp);//没有换行符'\n'
	puts("puts");

	//关闭文件
	fclose(fp);
	return 0;
}

代码执行结果如下:

3、按对象(二进制)输入输出

3.1 按对象输入输出函数声明:

//按对象输入,从stream中读取数据存入到ptr中
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//按对象输出,将ptr中的数据写入到stream中
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

返回值:成功返回读写对象的个数,出错返回EOF

ptr:缓冲区指针,用于存放读的数据或要写的数据

size:每个内容的大小(块大小) 单位:字节

nmemb:读写多少个内容

stream:进行操作的文件流指针

注意:这两个函数既可以读写文本文件,也可以读写二进制文件(数据文件)

注意:这两个函数执行后,光标都会进行移动 

3.2 读写文本文件实验

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	char buf[100] = {0};
	//1.打开文件
	fp = fopen("./file","a+");//以追加方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}

	fread(buf,sizeof(char),5,fp);//从fp指向的文件中读取5*sizeof(char)个字节,存放到buf中
	printf("read:%s\n",buf);

	//关闭文件
	fclose(fp);
	return 0;
}

代码运行结果如下:

3.3 读写二进制文件实验

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

struct test{
	char name[10];
	int age;
};

int main(){
	FILE* fp = NULL;
	struct test tWrite = {"rsy",20};
	struct test tRead;
	//打开文件
	fp = fopen("./file","a+");//以追加方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
    //从fp指向的文件中写入1*sizeof(struct test)个字节
	fwrite(&tWrite,sizeof(struct test),1,fp);
	//重置光标位置
	fclose(fp);
	if((fp = fopen("./file","a+")) == NULL){
		perror("fopen");
		return -1;
	}
    //从fp指向的文件中读取1*sizeof(struct test)个字节
	fread(&tRead,sizeof(struct test),1,fp);
	printf("name = %s,age = %d\n",tRead.name,tRead.age);
	//关闭文件
	fclose(fp);
	return 0;
}

代码运行结果如下:

流刷新定位

1、流刷新

流刷新的作用,就是手动的让缓冲区的内容进行输出,丢弃输入缓冲区的内容。

1.1 刷新流的函数声明如下:

int fflush(FILE *stream);

返回值:成功返回0,失败返回EOF

stream:指向文件的流

如果输出到屏幕,使用fflush(stdout)即可。

1.2 刷新流实验

#include <stdio.h>
#include <string.h>
#include <errno.h>

struct test{
	char name[10];
	int age;
};

int main(){
	FILE* fp = NULL;
	struct test tWrite = {"rsy",20};
	struct test tRead;
	//打开文件
	fp = fopen("./file","a+");//以追加方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
	//写入数据
	fwrite("1234",sizeof("1234"),1,fp);
	fflush(fp);//刷新流,这里的1234没有'\n',但刷新后可以将1234写入文件中
	while(1);

	//关闭文件
	fclose(fp);
	return 0;
}

2、流定位

流定位就是可以手动改变流指针(光标)的位置

注意:这下面3个函数只适用2GB以下的文件

2.1 流定位的函数声明如下:

//查询流指针位置
long ftell(FILE *stream);

返回值:成功返回流当前的读写位置,失败返回EOF

stream:要进行操作的文件流指针

注意:刚打开文件时,流指针的值为0,流指针的单位为字节。

//将流指针定位到开头
void rewind(FILE *stream);
//定位一个流
int fseek(FILE *stream, long offset, int whence);

返回值:成功返回0,失败返回 EOF

stream:要进行操作的文件流指针

offset:偏移量,正数代表向文件尾偏移,负数代表向文件头偏移,0代表不偏移

whence:偏移位置,从哪里开始偏移

参数含义
SEEK_SET从文件头开始偏移
SEEK_CUR从文件流指针当前位置开始偏移
SEEK_END从文件尾开始偏移

注意:当fopen时,模式为a时,fseek不起作用

2.2 流定位实验

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	char read_buf[100] = {0};
	//打开文件
	fp = fopen("./file","w+");//打开时清空,以覆盖方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
	//1.打开后,文件流指针为0	
	printf("after fopen,point = %ld\n",ftell(fp));
	//2.写入数据后,文件流指针偏移n个字节
	fwrite("12345",strlen("12345"),1,fp);
	fflush(fp);
	printf("after write 12345,point = %ld\n",ftell(fp));
	//3.写完后读取,需要将流指针重置位置
	rewind(fp);	
	fread(read_buf,100,1,fp);
	printf("now file data is %s\n",read_buf);
	memset(read_buf,0,sizeof(read_buf));
	//4.将流指针指向头,流指针变为0
	rewind(fp);
	printf("after rewind,point = %ld\n",ftell(fp));
	//写入是在流指针位置开始写入,这会覆盖数据
	fwrite("ab",strlen("ab"),1,fp);
	fflush(fp);
	rewind(fp);	
	fread(read_buf,100,1,fp);
	printf("now file data is %s\n",read_buf);	
	memset(read_buf,0,sizeof(read_buf));
	//5.fseek测试
	//将流指针指向ab345的b
	fseek(fp,1,SEEK_SET);
	fread(read_buf,100,1,fp);//读取之后流指针指向了5后面
	printf("after fseek->b,read data is %s\n",read_buf);
	memset(read_buf,0,sizeof(read_buf));
	fread(read_buf,100,1,fp);
	//printf("again read,read data is %s\n",read_buf);
	//memset(read_buf,0,sizeof(read_buf));
	//将流指针指向ab345的5
	fseek(fp,-1,SEEK_CUR);
	fread(read_buf,100,1,fp);
	printf("after fseek->5,read data is %s\n",read_buf);
	memset(read_buf,0,sizeof(read_buf));

	//关闭文件
	fclose(fp);
	return 0;
}

代码运行结果如下:

格式化输入输出

1、格式化输出

1.1 格式化输出的函数声明如下:

//将内容输出到屏幕
int printf(const char *format, ...);

//将内容输出到文件
int fprintf(FILE *stream, const char *format, ...);

//将内容输出到指定字符数组
int sprintf(char *str, const char *format, ...);

返回值:成功返回输出字符的个数,失败返回EOF

stream:要进行操作的文件流指针

str:要进行操作的字符数组指针

注意:fprintf也是从当前流指针位置开始写入

1.2 格式化输出实验

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	char buf[100] = {0};	
	//打开文件
	fp = fopen("./file","w+");//打开时清空,以覆盖方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
    //printf
	printf("printf:%s\n","this is printf");//将字符串打印到屏幕
	//sprintf
	sprintf(buf,"%s","this is sprintf");//将字符串写进buf中
	printf("sprintf:%s\n",buf);
	memset(buf,0,sizeof(buf));
    //fprintf
	fprintf(fp,"%s","this is fprintf");//将字符串写进fp指向的文件中
	fflush(fp);
	rewind(fp);
	fread(buf,sizeof(char),100,fp);
	printf("fprintf:%s\n",buf);
	memset(buf,0,sizeof(buf));
	//关闭文件
	fclose(fp);
	return 0;
}

代码运行结果如下:

2、格式化输入

2.1 格式化输入的函数声明如下:

//从标准输入上获取数据
int scanf(const char *format, ...);

//从文件中获取数据
int fscanf(FILE *stream, const char *format, ...);

//从字符串中获取数据
int sscanf(const char *str, const char *format, ...);

返回值:成功返回读取的数据个数,失败返回EOF

stream:从哪个文件获取数据

str:从哪个字符数组获取数据

注意:fscanf与sscanf会将存储空间先清空,再存入读取的值

注意:fscanf也是从当前流指针位置开始读取

sscanf这种格式化输入应用见b站视频,链接如下:

4.3.2蓝桥杯嵌入式组_串口接收数据解析(不定长)_哔哩哔哩_bilibili

2.2 格式化输入实验

具体代码实现如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(){
	FILE* fp = NULL;
	char buf[100] = {0};	
	//打开文件
	fp = fopen("./file","w+");//打开时清空,以覆盖方式,可读可写打开文件
	if(fp == NULL){
		perror("fopen");
		return -1;
	}
    //scanf
	scanf("%s",buf);
	printf("scanf:%s\n",buf);
	memset(buf,0,sizeof(buf));
    //sscanf
	sscanf("abcdef:123:xxx","%[^:]",buf);
	printf("sscanf:%s\n",buf);
	memset(buf,0,sizeof(buf));
    //fscanf
	fwrite("this is fscanf\n",strlen("this is fscanf\n"),1,fp);
	rewind(fp);
	fscanf(fp,"%s",buf);
	printf("fscanf:%s\n",buf);

	//关闭文件
	fclose(fp);
	return 0;
}

代码执行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值