【C语言】文件操作(下)

对比scanf\fscanf\sscanf,printf\fprintf\sprintf

在上一篇文件操作的介绍中,我向大家介绍了有关fscanf fprintf的用法,我们说他与scanf printf这两个函数很像,我们可以对比一下他们的函数声明:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看出来,他们参数的区别是fprintf fscanf多了一个文件指针,那么他们到底有什么区别呢?其实他们的作用都是以一定的格式输入输出数据,只是他们的对象不一样。

scanf : 按照一定的格式从键盘输入数据
printf : 按照一定的格式把数据输出到屏幕
适用于标准输入/输出流的格式化的输入/输出语句

fscanf : 按照一定的格式从输入流输入文件,这个输入流可以是文件中的也可以是键盘
fprintf : 按照一定的格式向输出流输出数据,可以是屏幕或者文件
适用于所有的输入、输出流的格式化输入、输出语句

今天还有一组输入输出的函数sscanf sprintf,让我们来看看这一组函数的作用是什么,与上面两组函数的区别又在哪里。
在这里插入图片描述
在这里插入图片描述
好了,我们说fscanf fprintfscanf printf 多了一个文件指针参数,那么fscanf fprintf 就可以读取文件中的数据,或者向文件中写入数据,而sscanf sprintf多了一个字符指针,是不是说明它可以将格式化数据向字符串中输入,或者可以从字符串中按照一定格式读取格式化数据

struct S {
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	char ch[100] = { 0 };
	struct S tmp = { 0 };

	struct S s = { "abc",10,"nan" };

	sprintf(ch,"%s %d %s", s.name, s.age, s.sex);//转换为为字符串输出
	printf("%s\n", ch);

	sscanf(ch, "%s %d %s", tmp.name, &(tmp.age), tmp.sex);
	printf("%s %d %s", tmp.name, tmp.age, tmp.sex);

	return 0;
}

在这里插入图片描述
根据上面的代码我们就可以总结出sscanf sprintf的作用

sscanf : 从字符串中按照一定的格式读取出格式化的数据
sprintf : 把格式化的数据按照一定的格式转换成字符串

这样函数有什么用呢?在我们以后的使用中,我们在前端页面获取信息时信息可能是一个字符串,传到后端后我们需要一个结构体数据,那么这一组函数是不是就可以完成这样的操作呢。

文件的随机读写

在上一篇文件操作的博客中,我们介绍了很多用于文件读写的函数,但那些库函数实现的都是按顺序读写文件,我们想读取任意位置的数据该如何实现呢?我们知道,文件内部内部的位置指针用来指示文件内部的当前读写位置,每读取一次,该指针向后移动一个位置。所以我们是不是可以改变这个指针位置,来实现读取任意位置的数据。这里我们就要向大家介绍一组库函数。

fseek

这个库函数可以根据文件指针的位置和偏移量来定位文件指针。

在这里插入图片描述
他的参数是FILE* stream文件流,long int offset偏移量,int origin从何位置开始偏移。返回值==如果成功,返回0,否则返回一个非零的值,在无法查找的设备上,返回值没有定义。我们在代码上演示一下这个函数。

int main()
{
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;

	}

	fseek(pf, 5, SEEK_SET);

	char ch = 0;

	ch = fgetc(pf);
	printf("%c", ch);

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

在这里插入图片描述
我们看见我们实现了打印从文件流开始向后偏移五个位置的数据'f'

ftell

当我们想使用fseek库函数但又不知道当下文件指针的位置的时,我们是不是需要打印一下现在文件指针的位置相较于起始位置的偏移量,我们就可以使用ftell函数来查看。
在这里插入图片描述

int main()
{
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;

	}

	fseek(pf, 5, SEEK_SET);

	char ch = 0;

	ch = fgetc(pf);
	printf("%c\n", ch);

	int address = 0;
	address = ftell(pf);
	printf("%d", address);

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述
这样就打印出了现在文件指针相较于起始位置的偏移量,有的同学会有问题了,我们刚才使用的fseek库函数,不是找到的是相较于起始位置偏移量为5的位置上的数据嘛,为什么此时我们使用ftell打印出来的偏移量是6呢?原因是,我们使用fseek之后读取了该位置的数据,所以我们的文件指针也向后移动一个位置,读取前距离起始位置的偏移量是5,读取后加一就变成6了

rewind

在这里插入图片描述
这个库函数的作用是让文件指针的位置回到起始位置

int main()
{
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;

	}

	fseek(pf, 5, SEEK_SET);

	char ch = 0;

	ch = fgetc(pf);
	printf("%c\n", ch);

	int address = 0;
	address = ftell(pf);
	printf("%d\n", address);

	rewind(pf);
	address = ftell(pf);
	printf("%d\n", address);

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

二进制文件与文本文件

数据是以二进制的形式在内存中存储的,对于文件来说,如果不加转换直接输出到外存的,就是二进制文件,如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储的文件就是文本文件
例如:我们想要存储10000这个数据,如果以ASCII码的形式输出到磁盘上,那么这个数的每一位都被看作一个字符,存储的是每个字符的ASCII码值,一共占5个字节,如果以二进制的形式输出到磁盘上,那么这个数就被看作一个整形,占4个字节,存储的是他的二进制形式。
在这里插入图片描述
我们可以在vs里面测试一下。

int main()
{
	int num = 10000;

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

	fwrite(&num, sizeof(num), 1, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

我们将10000这个数据以二进制的形式存储到文件中
在这里插入图片描述
我们看在文件中我们并不能看懂只是一个什么数据,因为他是以二进制存储的,我们使用我们的编译器来打开这个文件。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
我们看到这个文件从一堆乱码,变成了二进制的形式。

文件读取结束的判定

feof

这个库函数被很多人错误使用,用来判断文件是否读取结束,其实他的作用是判断文件读取结束的原因,是读取失败结束,还是遇到文件尾结束。

文件读取结束的原因有两种:
1.读取过程中出现异常
2.读取到文件末尾。
如何判断文件读取结束了呢:
文本文件
如果使用fgetc读取,要判断feof()的返回值是否为EOF。
如果使用fgets读取,要判断feof的返回值是否为NULL。
二进制文件
使用fread()读取,判断其返回值与指定读取个数的大小,如果小于实际要读的个数,就说明读取发生了异常,如果等于实际要读的个数,说明读到文件末尾结束了。对于读取异常的判断,我们要判断ferror()函数的返回值。
如果ferror()为真那么就是读取异常结束。
如果feof()为真那么就是读取到文件末尾结束。

文本文件的判断

int main()
{
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)//判断文件是否读取结束
	{
		putchar(ch);
	}
    // 判断文件为何读取结束
	if (ferror(pf))
	{
		printf("读取文件中发生了错误");

	}
	if (feof(pf))
	{
		printf("读取到文件末尾结束");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述
二进制文件判断

#define size 5
int main()
{
	int arr[size] = { 1,2,3,4,5 };
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(arr, sizeof *arr, size, pf);
	fclose(pf);

	int ret[size];
	pf = fopen("test.txt", "rb");
	size_t ret_num = fread(ret, sizeof *ret, size, pf);

	if (ret_num == size)//判断文件是否读取结束
	{
		printf("读取全部数据成功\n");
		int i = 0;
		for (i = 0; i < size; i++)
		{
			printf("%d", ret[i]);
		}
	}
	else//判断文件为何读取结束
	{
		if (ferror(pf))
		{
			printf("在读取中出现错误\n");
		}

		if (feof(pf))
		{
			printf("读取到文件末尾结束\n");
		}
	}

	return 0;
}

在这里插入图片描述

文件缓冲区

什么是文件缓冲区呢?其实就是一块内存空间,这块内存空间在使用文件时自动开辟,当我们从内存向磁盘输出数据时,数据会先存放在文件缓冲区中,等文件缓冲区满了后,一起输出给磁盘。当我们从磁盘向内存输入数据时,磁盘中的数据也是先读取到文件缓冲区,等到缓冲区充满,在从缓冲区逐个的将数据送到程序区,缓冲区的到小根据C语言编译系统决定,当然我们也可以手动刷新文件缓冲区,实现提前输入输出数据。
在这里插入图片描述

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悲伤猪小猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值