C语言文件操作(1)

🐵本篇文章将会对文件相关知识进行讲解


1. 为什么要使用文件❓

当我们写了一个简单的项目比如一个通讯录程序,我们添加完成员信息并关闭程序后,这些成员的信息也会随着程序的结束而不复存在了;所以为了使得数据持久化,我们将数据存放到文件中,因为文件是放在硬盘上的,数据不会丢失

2. 什么是文件❓

磁盘上的文件就是文件,在程序设计中我们讨论的文件有两种:程序文件,数据文件

2.1 程序文件

程序文件包括源程序文件(.c)目标文件(.obj)可执行程序(.exe)

2.2 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或者输出内容的文件(.txt就是数据文件

比如将通讯录成员的信息写到文件中,将文件中的信息读取出来;这里说的文件就是数据文件

2.3 文件名

每一个文件都要有唯一的文件标识也就是文件名,以便用户识别

文件名包含3部分:文件路径+文件主干名+文件后缀比如D:\code\data.txt

3. 文件的打开和关闭📂

3.1 文件指针

每一个使用过的文件都在内存上开辟了一块文件信息区,文件信息区实际上就是一个结构体,这个结构体用来存放文件的各种信息(比如:文件的名字、状态、位置等)该结构体类型是系统声明的,取名为:FILE

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并自动填充其中的信息,使用者不必关心细节
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。下面我们可以创建一个FILE* 的指针变量:

FILE* pf

pf就是一个指向FILE类型的指针,可以使pf指向某个文件的文件信息区(也可以说是一个结构体变量)通过pf可以找到文件信息区的信息,进而访问该文件;通过文件指针变量能够找到与它关联的文件

3.2 文件的打开和关闭

fopen函数原型:

fopen是以mode的方式打开文件filename,filename是文件名,可以字符串的形式表示,第二个参数是打开文件的方式,也是以字符串的形式表示

文件使用方式含义如果指定文件不存在
r(只读)为了(输入)读取数据,打开一个已存在的文本文件报错
w(只写)为了(输出)写入数据,打开一个文本文件建立一个新的文件

fclose函数原型:

参数stream是指向某个文件的指针,关闭文件后要将stream赋为空指针,以防止野指针

实例演示:

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

	return 0;
}

当使用fopen打开文件data.txt后,系统会自动创建一个FILE类型的变量,并向这个变量填充信息,之后将这个变量的地址传给pf

如果在当前路径(与.c文件相同的路径)没有data.txt,系统会自动创建一个同名文件

文件路径:文件路径有两种:绝对路径和相对路径

相对路径中.\\表示当前路径,..\\表示上一级路径

FILE* pf = fopen(".\\data.txt", "r"); //相对路径(此时是当前路径)

FILE* pf = fopen("C:\\Project\\C\\study\\study_10_8\\data.txt", "r"); //绝对路径(完整表述文件位置的路径)

4. 文件的顺序读写📋

功能函数适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流

4.1 流

向不同的外部设备传输数据的方法是不同的,为了简化这种传输方式,引入了流,我们可以将数据传输到流中,然后流会将数据传输到对应的外部设备里

流有文件流,标准输入流,标准输出流,标准错误流

操作文件前要先打开文件,操作完文件后要关闭文件,在使用scanf和printf时为什么没有打开键盘和打开屏幕的操作,这是因为C语言程序只要运行起来就会自动打开三个流:标准输入流(stdin)标准输出流(stdout)标准错误流(stderr),这三个流的类型也都是FILE*

4.2 函数讲解

fgetc函数原型:

fgetc用于读取文件的一个字符,stream是一个FILE*类型的指针,也可以理解为是一个流,fgetc如果读取失败则会返回EOF

实例演示:

事先向data.txt文件中输入abcdef

int main()
{
	FILE* pf = fopen("data.txt", "r");//读文件就要以只读的方式打开文件
    //写文件就要以只写的方式打开
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c", ch);
	}//结果为abcdef

	fclose(pf);
	pf = NULL;

	return 0;
}

fputc函数原型:

fputc用于向文件输入(写)一个字符,character是要输入的那一个字符,stream是一个FILE*类型的指针,也可以理解为一个流

实例演示:

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

	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch , pf); //输出到文件(写文件)
        fputc(ch, stdout); //输出到屏幕上,此时和printf功能相同
	}
	fclose(pf);
	pf == NULL;

	return 0;
}

4.3 其他函数讲解

功能函数适用于
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件流
二进制输出fwrite文件流
将格式化数据转化为字符串sprintf---
将字符串转化为格式化数据sscanf---

fgets函数原型:

fgets函数用于文本行输入,每次读取流中一行数据,即如果流中有多行数据,在读取其中的某一行时,当读取到'\n'就停止读取;fgets会从流stream中读取num - 1个字符到str中

FILE* pf = fopen("data.txt", "r");
char arr[100] = { 0 };
fgets(arr,num, pf); 
printf("%s", arr);

事先在data.txt中输入hello<换行>world,当num是5时,意思是将pf中5 - 1 = 4个字符读取到arr中,arr的打印结果为hell,当num为100时,会读取到第一行的'\n'所以会将"hello\n"读取到arr中

fputs函数原型:

fputs用于文本行输出,将str的数据写到流stream中,直到遇到'\0'

FILE* pf = fopen("data.txt", "w");
fputs("abcdef\0abcd", pf);

上述代码将"abcdef\0abcd"写到pf中,因为字符串中有'\0',所以只会将abcdef写到pf中,那么data.txt中的内容就是abcdef

fscanf函数原型:

fscanf用于格式化输入,直接看代码:

struct S
{
	float f;
	int i;
	char c;
};

FILE* pf = fopen("data.txt", "r");

struct S s = { 0 };
fscanf(pf, "%f %d %c", &(s.f), &(s.i), &(s.c));
printf("%f %d %c", (s.f), (s.i), (s.c));

事先在data.txt中输入3.14 5 c,上述代码就是读取pf中的数据以特定的格式到结构体s对应的变量中,最后打印结果为3.140000 5 c

fprintf函数原型:

fprintf函数用于格式化输出,直接看代码:

struct S
{
	float f;
	int i;
	char c;
};

FILE* pf = fopen("data.txt", "w");

struct S s = {3.14, 5, 'c'};
fprintf(pf, "%f %d %c", s.f, s.i, s.c);

上述代码是将结构体变量初始化的数据以特定的格式写入文件data.txt中


文件使用方式含义如果指定文件不存在
rb(只读)为了输入数据,打开一个二进制文件出错
wb(只写)为了输出数据,打开一个二进制文件建立一个新的文件

fwrite函数原型:

fwrite是对一个二进制文件写入数据,fwrite的功能是将ptr中的内容写到stream中,size是写入数据的字节大小,count是写入的元素个数

FILE* pf = fopen("data.txt", "wb");

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
fwrite(arr, sizeof(int), sizeof(arr)/sizeof(arr[0]), pf);

在data.txt中显示的内容是:

这些数据我们无法理解其含义,所以要通过fread将该二进制文件中的数据读取出来并打印到屏幕上(我们向文件中写的是1~10,那么读取后打印到屏幕上的也应该是1~10)

fread函数原型:

fread是读取一个二进制文件中的数据,fread的功能是将stream中的数据读取到ptr中,stream中每一个数据的字节大小是size,一共读取count个元素

FILE* pf = fopen("data.txt", "rb");

int arr1[10] = { 0 };
fread(arr, sizeof(int), 10, pf);

int i = 0;
for (i = 0; i < 10; i++)
{
	printf("%d ", arr1[i]);
}

打印结果为:

sprintf函数原型:

sprintf用于将格式化的数据转化为字符串

sscanf函数原型:

sscanf用于将字符串转化为格式化数据

struct S
{
	float f;
	char c;
	int i;
};

int main()
{
	struct S s = { 3.14, 'c', 100 };
	char arr[100] = { 0 };
	sprintf(arr, "%f %c %d", (s.f), (s.c), (s.i));
	printf("%s\n", arr);

	struct S t = { 0 };
	sscanf(arr, "%f %c %d", &(t.f), &(t.c), &(t.i));
	printf("%f %c %d\n", t.f, t.c, t.i);
	return 0;
}

打印结果为:

我们可以通过调试观察arr

4.4 对比总结

接下来通过对比来总结printf, scanf, fprintf, fscanf, sprintf, sscanf这六个函数

printf:格式化输出函数,适用于标准输出流(屏幕)

scanf:格式化输入函数,适用于标准输入流(屏幕)

fprintf:格式化输出函数,适用于所有输出流(文件流,标准输出流)

fscanf:格式化输入函数,适用于所有输入流(文件流,标准输入流)

sprintf:将格式化数据转化为字符串

sscanf:将字符串转化为格式化数据


🙉至此,对文件的第一部分讲解结束,接下来会继续讲解文件相关知识

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值