【c语言】文件操作全解(中)

接上文,接下来学习文件的读写操作,不用想的太复杂,其实很简单,就是在学习函数的使用罢了。本文所涉及函数都是根据官网https://cplusplus.com/所得。

文件读/写操作,分为顺序读写和随机读写。

记得进行文件读写操作时,要先用fopen函数打开文件,之后要用fclose函数关闭文件。

我们先来学习文件的顺序读写。

目录

4. 文件顺序读写操作

4.1 fgetc和fputc函数

4.1.1 getc函数

4.1.2 fputc函数

4.2 fgets和fputs函数

4.2.1 fgets函数

4.2.2 fputs函数

4.3 fscanf和fprintf函数

4.3.1 fscanf函数

4.3.2 fprintf函数

4.4 fread和fwrite函数

4.4.1 fwrite函数

4.4.2 fread函数


4. 文件顺序读写操作

文件顺序读写,所涉及的函数如下:

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

这些函数的头文件是<stdio.h>。

大家不要被这么多函数吓到了,其实大同小异,但是有些区别。

4.1 fgetc和fputc函数

这一组的两个函数用于单个字符。

4.1.1 getc函数

fgetc函数是字符输入函数。在使用输入函数时,打开文件用fopen函数,第二个参数要用读"r"

该函数一般用来读取文件中单个字符。读取完一个字符后,文件指针(文件内容的光标)将会前进到下一个字符。

若读取成功,返回字符的ASCII码值;若读取到文件末尾或者读取失败时,返回EOF.

为什么它返回类型是int呢,是为了兼容EOF(-1)的情况。

我先在文件里放:

很显然刚开始文件指针在最初的位置,使用fgetc函数读一个字符,文件指针就往下一个位置。

运行代码,就会得到:

4.1.2 fputc函数

 打开文件用fopen函数,第二个参数要用写"w"。

fputc函数一般作用是向文件写入单个字符。

注意,该函数第一个参数类型是int,用来接收想要写入文件的字符的ASCII码值;第二个参数是文件指针变量。

返回类型是int。如果成功,则返回该字符(第一个参数)的ASCII码值;如果失败,则返回EOF。

操作一下:

写一下26个字母。

4.2 fgets和fputs函数

前一组的两个函数只能单个字符,这一组的这两个函数用于字符串,可以更快一点。

4.2.1 fgets函数

fgets函数是文本行输入函数,在使用输入函数时,打开文件用fopen函数,第二个参数要用读"r"

fgets函数作用是读取文件的字符串。

第一个参数类型是char*,是一个指向char类型的指针,是你读取数据后想要放入的地方

第二个参数类型是int,是读取的最大字符数(包括最后的空字符’\0’),单位是字节。因为包括了空字符,所以你实际读取的数据是num-1,最后一个字符一定是放'\0'。

第三个参数是文件指针变量。

返回值是char*类型,若读取成功,则返回str;若读取失败,则返回NULL。

操作一下,我先在文件里放入:

现在我要读取文件的第一行字符串,把它放在str数组中,并打印到显示器上。

如果fgets函数第二个参数放的是26,那么就只会读到25个字母。

如果在读取中,提前遇到了\0,那么fgets函数不会再往下读了,它会停止读取字符,并在已读取字符的末尾添加空字符’\0’,作为字符串结束符。

下图,fgets函数第二个参数是30,按理应该读完26个字母,加上1个换行符,还有两个字母‘h’、‘e’;

但事实上我只读到了26个字母和一个换行符,就停止了,fgets函数就已经在末尾加上了\0。

这也是fgets函数叫做文本行输入函数的原因了。

4.2.2 fputs函数

fputs函数是文本行输出函数,在使用输出函数时,打开文件用fopen函数,第二个参数要用写"w"

fputs函数一般作用是向文件写入指定的字符串。

第一个参数类型是char*,是一个指向char类型的指针,它也可以是字符串常量。它是你想写进文件里的字符串首字母地址

第二个参数是文件指针变量。

返回类型是int,如果写入成功,会返回一个非负数值;如果写入失败,会返回EOF(-1)。

它直到遇到\0停下。

操作一下:

如果提前遇到\0,就会提前停下。

4.3 fscanf和fprintf函数

4.3.1 fscanf函数

fscanf函数是格式化输入函数。在使用输入函数时,打开文件用fopen函数,第二个参数要用读"r"

其实fscanf函数和scanf函数很像,你会使用scanf函数其实也就是会使用fscanf函数。

对比一下两个函数,你会发现,其实fscanf函数也就比scanf函数多了第一个参数,文件指针变量。

两个函数后面的省略号参数,是接收所要输入的数据的变量。

两个函数返回类型是int,如果读取成功的话,都代表着读取数据的个数(大于等于0);若读取失败,则返回EOF(-1)。

注意:使用fscanf和scanf函数时,非字符串类型的所要储存输入数据的变量需要&(取地址)。

操作一下:

文件里放入:

分别使用一下scanf函数和fscanf函数。

其实你就会发现,他俩长得太像了,直接复制粘贴scanf函数,稍微改一下名字,再加上第一个参数pf,摇身一变就是fscanf函数。

运行代码:

struct Man
{
	char name[20];//名字
	char sex[10];//性别
	int age;//年龄
};

int main()
{
	//使用scanf函数
	printf("使用scanf函数。\n");
	struct Man man1;
	scanf("%s %s %d", man1.name, man1.sex, &(man1.age));
	printf("名字:%s 性别:%s 年龄:%d\n", (man1.name), (man1.sex), man1.age);

	printf("\n");

	//使用fscanf函数读
	printf("使用fscanf函数。\n");
	struct Man man2;
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	assert(pf);

	//文件读操作
	fscanf(pf,"%s %s %d", man2.name, man2.sex, &(man2.age));
	printf("名字:%s 性别:%s 年龄:%d\n", (man2.name), (man2.sex), man2.age);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

既然这么像,那么如何让fscanf函数变成scanf函数呢?请大家参考我上篇博客文件操作全解(上)中提到的流和标准流的概念,这样就会清楚了解到scanf函数的逻辑。

我们只要把fscanf函数的第一个参数改为stdin,就是一模一样了这两个函数。

运行代码:

struct Man
{
	char name[20];//名字
	char sex[10];//性别
	int age;//年龄
};

int main()
{
	//使用scanf函数
	printf("使用scanf函数。\n");
	struct Man man1;
	scanf("%s %s %d", man1.name, man1.sex, &(man1.age));
	printf("名字:%s 性别:%s 年龄:%d\n", (man1.name), (man1.sex), man1.age);

	printf("\n");

	//使用fscanf函数
	printf("使用fscanf函数。\n");
	struct Man man2;
	fscanf(stdin,"%s %s %d", man2.name, man2.sex, &(man2.age));
	printf("名字:%s 性别:%s 年龄:%d\n", (man2.name), (man2.sex), man2.age);

	return 0;
}

4.3.2 fprintf函数

fprintf函数是格式化输出函数。在使用输出函数时,打开文件用fopen函数,第二个参数要用写"w"

我们一样对比一下printf函数。

跟fscanf函数一样,fprintf函数和printf函数很像,fprintf函数只比printf函数多了第一个参数,文件指针变量。

两个函数后面的省略号参数,是接收所要输入的数据的变量。

返回类型是int,若成功运行,会返回所输出的字符字节总数;若失败,会返回一个负数并报错。

操作一下(文件此时为空):

struct Man
{
	char name[20];//名字
	char sex[5];//性别
	int age;//年龄
};
int main()
{
	//使用printf函数
	printf("使用printf函数\n");
	struct Man man1 = {"张三","男",18};
	printf("名字:%s 性别:%s 年龄:%d\n", (man1.name), (man1.sex), (man1.age));

	printf("\n");
	//使用fprintf函数
	printf("使用fprintf函数\n");
	struct Man man2 = { "李四","男",19 };

	//打开文件
	FILE* pf = fopen("test.txt", "w");
	assert(pf);

	//文件写操作
	fprintf(pf,"名字:%s 性别:%s 年龄:%d\n", (man2.name), (man2.sex), (man2.age));

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果:

然后你也会发现,他俩长得太像了,直接复制粘贴printf函数,稍微改一下名字,再加上第一个参数pf,摇身一变就是fprintf函数。

一样的道理,fprintf函数也可以转化成printf函数,只要把第一个参数改为stdout就可以了。这样就可以直接打印在显示器上了。

运行代码:

//fprintf函数怎样变成printf函数
struct Man
{
	char name[20];//名字
	char sex[5];//性别
	int age;//年龄
};

int main()
{
	//使用printf函数
	printf("使用printf函数\n");
	struct Man man1 = {"张三","男",18};
	printf("名字:%s 性别:%s 年龄:%d\n", (man1.name), (man1.sex), (man1.age));

	printf("\n");
	//使用fprintf函数
	printf("使用fprintf函数\n");
	struct Man man2 = { "李四","男",19 };
	fprintf(stdout,"名字:%s 性别:%s 年龄:%d\n", (man2.name), (man2.sex), (man2.age));

	return 0;
}

结果:

到这里,我们通过对比可以知道一件事情:

scanf函数标准输入流stdin上读取格式化的数据
fscanf函数指定的输入流上读取格式化的数据
printf函数把数据以格式化的形式打印在标准输出流stdout
fprintf函数把数据以格式化的形式打印在指定的输出流

这样我们的理解可以更深刻一些。

4.4 fread和fwrite函数

这一组函数用于以二进制的形式输入或输出文件(注意,这组函数只适用于文件!)。

4.4.1 fwrite函数

fwrite函数是二进制输出函数。在使用输出函数时,打开文件用fopen函数,第二个参数要用写"wb"

该函数有四个参数。

第一个参数类型是void*(它用来接收任意类型,什么类型取决于程序员自己),是将要输出数据的变量的指针。

第二个参数类型是size_t(用来接收sizeof的返回值),以字节为单位,是文件读取数据基本单元字节的大小。

第三个参数类型是size_t,是读取的次数,读取数据的数量。

第四个参数是文件指针变量。

返回类型是size_t,若成功运行,会返回输出的数据总数。如果此数字与 count 参数不同,则会报错误来阻止函数完成。若大小为零,则该函数返回零,同时错误指示器ferror保持不变。

操作一下(此时文件为空):

运行代码:

int main()
{
	//使用fwrite函数
	//打开文件
	FILE* pf = fopen("test.txt", "wb");
	assert(pf);

	//文件写操作
	int str[10] = { 1,2,3,4,5 };
	fwrite(&str,sizeof(int), 5, pf);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

我们可以发现记事本文件中所读取的数据我们肉眼是看不懂的,因为我们写入文本的是二进制,而文件是以文本形式读取的,所以是乱码。

4.4.2 fread函数

fread函数是二进制输入函数。在使用输入函数时,打开文件用fopen函数,第二个参数要用读"rb"

该函数有四个参数。

第一个参数类型是void*(它用来接收任意类型,什么类型取决于程序员自己),是将要放入数据的变量的指针。

第二个参数类型是size_t(用来接收sizeof的返回值),以字节为单位,是文件读取数据基本单元字节的大小。

第三个参数类型是size_t,是读取的次数,读取数据的数量。

第四个参数是文件指针变量。

返回类型是size_t,若运行成功,会返回读取数据的次数;若失败,返回0。

操作一下:

此时文本内容是刚才以二进制形式写入的数字12345,我们新建的str数组里面什么都没有。

运行代码:

int main()
{
	//使用fwrite函数
	//打开文件
	FILE* pf = fopen("test.txt", "rb");
	assert(pf);

	//文件读操作
	int str[10] = { 0 };
	if ((fread(str, sizeof(int), 5, pf))) 
	{
		for (int i = 0; i < 5; i++)
		{
			printf("%d ",str[i]);
		}	
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

还有文件的随机读写,文件读取结束的判定和文件缓冲区等知识,我们下一篇博客见~

  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值