有关C语言中文件的操作

这篇文章将总结c语言中一系列关于结构体的知识点。

如果您对这篇文章内容感到满意,可以点进我的主页查看更多相关内容。

我的主页:OMGmyhair的优快云博客主页

一、 流

C 语言中,术语 stream )表示任意输入的源或任意输出的目的地。这里我们可以将其理解为,我们将信息数据输入到流中,当我们又需要信息数据时,我们也可以从流中进行拿取。

二、文件指针

当我们打开一个文件时,会有一块FILE类型的文件信息区专门显示这个文件的信息,我们用一个指针指向这个文件信息区。我们对流的访问,就是通过文件指针进行实现的,这个指针类型为FILE*。

三、标准流

c语言中<stdio.h>提供了3个标准流,这三个标准流可以直接使用,不需要对其进行声明,也不用打开或关闭。
文件指针默认的含义
stdin标准输入键盘
stdout标准输出屏幕
stderr标准误差屏幕


四、文件操作函数

1.fopen函数

当我们想要打开文件的时候,可以用fopen函数进行打开文件。
这里我们可以看到,这个函数的返回值是 FILE*类型,有两个参数,第一个参数是你想要打开的文件的名字,第二个参数是你打开文件的模式,我们稍后会介绍文件模式。当文件打开失败,会返回一个 NULL指针

2.fclose函数

关闭文件,我们可以用fclose函数进行关闭已经打开的文件。
这个函数的参数为文件指针。
如果文件成功关闭,则会返回一个0值。
如果关闭失败,则返回EOF。

<模式>

*号表示从C11开始引入的模式(独占的创建-打开模式)
用于文本文件的模式字符串:
字符串含义
"r"打开文件用于读
"w"打开文件用于写(文件不需要存在)
"wx"创建文件用于写(文件不能已经存在)*
"w+x"创建文件用于更新(文件不能已经存在)*
"a"打开文件用于追加(文件不需要存在)
"r+"打开文件用于读和写,从文件头开始
"w+"打开文件用于读和写(如果文件存在就截去)
"a+"打开文件用于读和写(如果文件存在就追加)

 用于二进制文件的模式字符串

字符串含义
"rb"打开文件用于读
"wb"打开文件用于写(文件不需要存在)
"wbx"创建文件用于写(文件不能已经存在)*
"ab"打开文件用于追加(文件不需要存在)
"r+b"或"rb+"打开文件用于读和写,从文件头开始
"w+b"或"wb+"打开文件用于读和写(如果文件存在就截去)
"w+bx"或"wb+x"创建文件用于更新(文件不能已经存在)*
"a+b"或"ab+"打开文件用于读和写(如果文件存在就追加)

3.顺序读写函数

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

(1)fprintf函数

你可能会觉得这个函数跟printf函数长得很像,我们来看一看它的定义:

我们可以用这个函数进行格式化的输出,从上面的表格我们可以看出,它针对的是所有输出流,这里我们不仅能将数据输出到文件中去,还能将其输出到屏幕上,我们照样用两个实例来加深理解:

首先输出到文件中去:

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int a = 0;
	for (int i = 0; i < 10; i++)
	{
		fprintf(pf,"%d", i);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

此处我们将0~9个数字都输出到文件:

现在我们再试试输出至屏幕上(这里我们需要用到文件指针stdout,它维护的是标准输出流):

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int a = 0;
	for (int i = 0; i < 10; i++)
	{
		fprintf(stdout, "%d", i);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

(2)fscanf

我们来看看fscanf函数的定义

可以看到这个函数的功能是从流中拿取格式化的数据,跟fprintf类似,这个函数也可以从屏幕上读取数据,或者从文件中读取数据,我们举两个实例:

从文件中读取数据:

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int a ;
	
	for (int i = 0; i < 10; i++)
	{
		fscanf(pf, "%d", &a);
	}
	
		printf("%d", a);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

注意在用fscanf的时候,要注意&符号的使用。

我们再试试让其从屏幕上读取数据

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int a ;
	fscanf(stdin, "%d", &a);
	printf("%d", a);
	fclose(pf);
	pf = NULL;
	return 0;
}

那我们是否可以从屏幕输入数据到文件呢?让我们来试试

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		fscanf(stdin, "%d", &arr[i]);
	}
		for (int i = 0; i < 10; i++)
	{
		fprintf(pf, "%d", arr[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

这里我们向屏幕输入9876543210

查看文件:

 

要注意不能直接像下面一样写:

 需要注意的是fscanf函数不能用于两个流之间

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fscanf(stdin, "%d", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

(3)fputc

我们来看看fputc函数的定义

我们可以看到fputc函数的功能是将一个字符写入流中,我们来举个实例:

这里我们将26个英文字母放入文件中。

int main()
{
	FILE* pf = fopen("text.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char a = 'a';
	for (int i = 0; i < 26; i++)
	{
		fputc(a+i, pf);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

需要注意的是:当文件以"w"的形式打开,文件原先的内容会被清空。

写入26个英文字母后,我们打开文件查看一下:

(4)fgetc

我们来看看这个函数的定义

可以看出这个函数的功能是从流中拿一个字符。

我们基于fputc函数的实例,再从文件从拿出26个字符。

int main()
{
	FILE* pf = fopen("text.txt","r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char a ;
	for (int i = 0; i < 26; i++)
	{
		printf("%c", fgetc(pf));
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

注意此时是以"r"模式即读的方式拿出文件中的字符。

(5)fputs 

我们来看这个函数的定义

可以看到这个函数的功能是向流中写入一个字符串。如果写入成功,返回一个非负值。如果写入失败,返回EOF。

实例如下:我们向文件中写入“hello world”

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char a[100]="hello world";
	fputs(a, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

(6)fgets

我们来看这个函数的定义

我们可以看到这个函数有3个参数,功能是从流中拿取字符串,第二个参数num是从流中拿取字符的个数。如果读取成功函数返回值是返回拿取的字符串的首地址,若读取错误或者在读取到任意一个字符前遇到了文件末尾,则返回一个空指针。我们照样举一个实例。

注意:fgets这个函数实际上只会读取num-1个字符,因为最后一个字符的位置编译器会用来放\0

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char a[100];
		printf("%s", fgets(a,26,pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

(7)sscanf

我们来看看这个函数的定义:

这个函数功能是在字符串中读取格式化的数据,见实例:

int main()
{
	char arr[30] = "this is 2024!";
	char str[20];
	char strr[20];
	int i;
	sscanf(arr, "%s %s %d", str,strr, &i);
	printf("%s\n",str);
	printf("%s\n", strr);
	printf("%d\n", i);
	return 0;
}

结果如下:

 (8)sprintf

我们来看看sprintf函数的定义:

我们可以看到这个函数的作用是将格式化的数据写入字符串,举个实例看看:

int main()
{
	char arr[30];
	char str[20]="say";
	char strr[20] = "so";
	int i = 25;
	sprintf(arr, "%s %s %d", str, strr, i);
	printf("%s", arr);
	return 0;
}


 scanf、fscanf、sscanf和printf、fprintf、sprintf的区别

你可能一眼就能看出来,这些函数长得非常相似,那它们的区别在哪里呢?见下表:

函数功能
scanf从标准输入流上读取格式化的数据
fscanf从指定的输入流上读取格式化的数据
sscanf在字符串中读取格式化的数据
printf把数据以格式化的形式打印在标准输出流上
fprintf把数据以格式化的形式打印在标准输出流上
sprintfsprintf把格式化的数据转化成字符串

五、文件位置相关函数

(1)fseek函数

我们来看看fseek函数的定义:

我们可以看到,这个函数的功能是重新定位流位置指示器(即光标)

第一个参数是指向待修改的文件的文件指针

第二个参数是偏移量,向左偏移则为负数,向右偏移则为正数(以字节为单位)

第三个参数是开始偏移的起始位置,<stdio.h>对从文件末尾、光标当前位置还是文件起始位置开始偏移定义了3个宏,如下:

SEEK_SET文件的起始处
SEEK_CUR文件的当前位置
SEEK_END文件的末尾处

我们依旧举一个实例看看,此时待读取的文件里面放的是myworld,初始的时候光标在内容的最前面:

此时,我们让光标向后移动两个位置,对world进行输出:

代码如下:

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf,2, SEEK_SET);
	char a[100];
	printf("%s", fgets(a, 6, pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

  (2)ftell

此时我们再来看看ftell函数,定义如下:

 可以看到,这个函数的功能是获取当前光标在流中的位置,我们基于上面fseek函数向右偏移两个位置的情况来举例,实例如下:

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf,2, SEEK_SET);
	/*char a[100];
	printf("%s", fgets(a, 6, pf));*/
	printf("%d", ftell(pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

此时是否会输出2呢?

 如我们所想,输出的是2。

(3)rewind

这个函数的功能是将文件位置(光标)设置在起始处

注:调用 rewind(fp)几乎等价于 fseek(fp, 0L, SEEK_SET),两者的差异是 rewind 函数不返回值,但会为 fp 清除错误指示器。

我们也为这个函数举一个实例:

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 2, SEEK_SET);
	printf("fseek偏移2个后,位置在:%d处\n", ftell(pf));
	rewind(pf);
	printf("rewind函数后,位置在:%d处", ftell(pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

(4)feof

我们可以看到feof函数是用来检查文件末尾的函数,当文件是遇到末尾时,它会返回一个非0值,其它情况则返回一个0。

(5)ferror

这个函数用于检测读取是否发生错误,当读取发生错误时,返回一个非0值,其它情况返回一个0。

六、文件缓冲

向磁盘驱动器传入数据或者从磁盘驱动器传出数据都是相对较慢的操作。因此,在每次程
序想读或写字符时都直接访问磁盘文件是不可行的。获得较好性能的诀窍就是 缓冲 buffering
把写入流的数据存储在内存的缓冲区域内;当缓冲区满了(或者关闭流)时,对缓冲区进行“清
洗”(写入实际的输出设备)。输入流可以用类似的方法进行缓冲:缓冲区包含来自输入设备的
数据;从缓冲区读数据而不是从设备本身读数据。

这里有两个函数可以对缓冲区进行 清洗/刷新:fflush、fclose。

是的,fclose在关闭文件时,也会刷新缓冲区。

练习:复制文件

使用上面讲到的内容,将其中一个文件中的内容,复制到另外一个文件中去。

int main()
{
	FILE* pf1 = fopen("practice.txt", "r");
	FILE* pf2 = fopen("practice_copy.txt", "w");
	if (pf1 == NULL)
	{
		perror("fopen1");
		return 1;
	}
	if (pf2 == NULL)
	{
		perror("fopen2");
		return 1;
	}
	int c=0;
	while ((c=fgetc(pf1) )!= EOF)
	{
		fputc(c, pf2);
	}
	fclose(pf1);
	pf1 = NULL;
	fclose(pf2);
	pf2 = NULL;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值