C语言文件操作全攻略:让你的程序拥有记忆!

目录

引言

一、什么是文件?

1.1、程序文件

1.2、数据文件

1.3、文件名

二、二进制文件和文本文件

三、文件的打开

3.1、流

3.2、标准流

3.3、文件指针

3.4、文件的打开和关闭

3.4.1、 fopen

3.4.2、fclose

四、文件的顺序读写

4.1、 fputc

4.2、 fegtc

4.3、 feof 和 ferror

(1) feof

(2) ferror

4.4、 fputs

4.5、 fgets

4.6、 fprintf

4.7、 fscanf

4.8、 fwrite

4.9、 fread

4.10、 sprintf 和 sscanf

4.10.1、 sprintf

4.10.2、 sscanf

4.11scanf/printf 和 fscanf/fprintf 和 sscanf/sprintf

五、文件的随机读写

5.1、 fseek

5.2、 ftell

5.3、rewind

六、文件缓冲区

6.1、 fflush 

结语


引言

想象一下,你辛辛苦苦运行程序产生的宝贵数据,如果每次关闭程序就消失得无影无踪,是不是很让人沮丧?好在,C语言的文件操作功能就像一扇神奇的大门,它赋予了你的程序持久保存和读取数据的能力。通过文件操作,你可以将程序运行的结果保存到硬盘中,以便日后分析和使用,也可以从已有的文件中读取数据,作为程序的输入。

文件操作是C语言学习中不可或缺的一部分,更是程序开发中一项基础而重要的技能。无论你是想开发一个简单的文本编辑器,还是处理复杂的科学数据,都离不开对文件的读写操作。

本文将由浅入深地带你探索C语言的文件操作,让你从零开始,逐步掌握文件的打开、读取、写入、关闭等基本操作,以及文件指针的灵活运用。无论你是C语言新手,还是有一定基础的开发者,相信都能从中受益,告别文件操作的困惑,让你的程序拥有更强大的数据处理能力!

一、什么是文件?

说白了,文件就是硬盘上的文件。我知道这句话跟说了没说,但是你先别急。

文件分为:

1.1、程序文件

程序文件分为:源程序文件(.c),目标文件(.obj)以及可执行程序文件(.exe)

源程序文件又称源文件,就是我们编写代码创建的那个;

目标文件就是源文件经过编译编译后产生的文件;

可执行程序就是目标文件经过链接器链接后产生的,可执行程序顾名思义是可以执行一些功能的

1.2、数据文件

看意思就知道,数据文件是用来存放某些数据的,而非程序

1.3、文件名

文件名包括:文件路径+文件名主干+文件后缀

比如:C:\keilproject\parctice1.c

二、二进制文件和文本文件

(1)、数据在内存中以二进制形式存储,如果不加转换的输出到外存的文件中,就是二进制文件

(2)、如果要求在外存的文件上以ASCII码的形式存储,就是文本文件

简单来说,你打开一个文件,如果你能看懂,就是文本文件,如果是一堆你看不懂的乱码,就是二进制文件

三、文件的打开

3.1、流

在C语言的文件操作中,“流”(Stream)是一个非常重要的概念。可以把“流”想象成一个连接程序和外部设备(例如硬盘上的文件,或者键盘、显示器)的管道。数据就像水流一样,通过这个管道在程序和外部设备之间传输。

C语言中有两种类型的流:

  • 文本流: 文本流处理的是文本数据,也就是字符序列。在文本流中,某些字符可能会被转换,例如,换行符在不同的操作系统中表示方式不同,文本流会自动进行转换。

  • 二进制流: 二进制流处理的是原始的字节数据,不会进行任何转换。二进制流可以用于处理任何类型的数据,包括文本、图像、音频等。

3.2、标准流

·stdin —— 标准输入流,通常连接到键盘,从键盘读取数据

·stdout——标准输出流,通常连接到显示器,用于向显示器输出数据

·stderr——标准错误流,通常连接到显示器,用于输出错误信息

这三个流的类型:FILE* ,通常成为文件指针

3.3、文件指针

每个被使用的文件都在内存中开辟了一个相对应的文件信息区,用来存放文件的相关信息。这些信息保存在一个结构体变量中,该结构体类型是由系统声明的,取名FILE

例如:创建一个 FILE* 的指针变量

FILE* pf;  //文件指针变量

3.4、文件的打开和关闭

3.4.1、 fopen

原型:

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

功能:打开 filename 指定的文件,同时将打开的文件和一个流关联,同时通过 mode 来指定操作

返回:文件打开成功,返回一个文件指针;

           失败,返回NULL

mode 是打开文件的模式,通常有以下几类:

文件使用方式含义如果指定文件不存在
“r” (只读)为了读取数据,打开一个文本文件

出错

“w” (只写)为了写入数据,打开一个文本文件建立一个新文件
“a” (追加)在文本末尾添加数据建立一个新文件
“rb” (只读)为了读取数据,打开一个二进制文件出错
“rw” (只写)为了写入数据,打开一个二进制文件建立一个新文件
“ab” (追加)向一个二进制文件尾添加数据建立一个新文件
“r+” (读写)打开一个文本文件出错
“w+” (读写)打开一个文本文件建立一个新文件
“a+” (读写)在文件末尾读写建立一个新文件
“rb+” (读写)打开一个二进制文件出错
“wb+” (读写)打开一个二进制文件建立一个新文件
“ab+” (读写)二进制文件尾读写建立一个新文件

举个栗子:

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

这个文本前提是你已经创建好的,并且在项目目录下。如果在其他地方,要记得写出完整的路径。

Note:相对路径,一个点表示当前路径,两个点表示上一级路径

例如:./../data.txt  表示当前路径的上一级目录下的 data.txt 文件

3.4.2、fclose

原型:

int fclose(FILE* stream);

功能:关闭关联文件

返回:成功返回 0; 失败返回 EOF

例如:

fclose(pf);
pf = NULL;

将 pf 置为 NULL 是为了避免野指针的出现

四、文件的顺序读写

4.1、 fputc

原型:

int fputc(int character, FILE* stream);

功能:将 character 指向的字符写入到 stream 指向的输出流中

返回:成功,返回写入的字符(以 int 形式);

           失败,返回 EOF

例如:

int main()
{
	FILE* pf = fopen("data.txt","w");//以写的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('a' ,pf);
	fputc('b', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

那么该文件中就会写入 ab:

Note:写字符到标准流

fputc('a' ,stdout);
fputc('b' ,stdout);

stdout 是标准输出流,相当于在屏幕上打印字符

4.2、 fegtc

原型:

int fgetc(FILE* stream);

功能:从 stream 指向的流中读取一个字符

返回:成功,返回读取到的字符(以 int 的形式);

           失败,返回 EOF

举个栗子:

int main()
{
	FILE* pf = fopen("data.txt", "r");//以写的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int n1 = fgetc(pf);
	int n2 = fgetc(pf);
	printf("%c %c" ,n1 ,n2);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

4.3、 feof 和 ferror

(1) feof

原型:

int feof(FILE* stream);

功能:检测 stream 指向的流是否遇见文件末尾

返回:检测到指示符返回非0;

           检测不到返回 0

(2) ferror

原型:

int ferror(FILE* stream);

功能:检测 stream 指向的流知否发生读/写错误

返回:检测到返回非0;没检测到返回 0

举个栗子:

if(feof(pf))
    printf("遇到文件末尾"\n);
else if(ferror(pf))
    printf("读取发生了错误\n");

4.4、 fputs

原型:

int fputs(const char* str, FILE* stream);

功能:将 str 指向的字符串写入 stream 指定的流中

返回:成功,返回非负整数;

           失败返回 EOF

举个栗子:

int main()
{
	FILE* pf = fopen("data.txt", "w");//以写的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fputs("sichenglang" ,pf);
	fputs("budehaosi" ,pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

值得注意的是,以w打开后原本的 ab 被覆盖掉了:

这是因为,以 w 打开,会把文件以前的全部数据清空,而不是覆盖!

4.5、 fgets

原型:

char* fgets(char* str, int num, FILE* stream);

功能:从 stream 指定的输入流中读取字符串,直到读取到换行符、文件末尾或者 ‘\0’ ,然后将读取到的字符串存储到 str 指向的空间中

num:要读取的长度(包括 '\0')

返回:成功返回 str 指针;失败返回 EOF

举个栗子:

int main()
{
	FILE* pf = fopen("data.txt", "r");//以读的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[30];
	fgets(arr, 30, pf);
	printf("%s" ,arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

4.6、 fprintf

原型:

int fprintf(FILE* stream, const char* format, ……);

 功能:将格式化数据写入指定文件流中

返回:成功,返回写入字符的总数;

           失败返回负值

举个栗子:

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	FILE* pf = fopen("data.txt", "w");//以写的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	struct Stu n1 = { "matongtong" , 19 ,88.0 };
	fprintf(pf, "%s %d %f\n" ,n1.name ,n1.age, n1.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

那么文件就被写入了:

4.7、 fscanf

原型:

int fscanf(FILE* stream, const char* format , ……);

功能:从指定的文件流中读取格式化数据

返回:成功返回读取的项数;

           失败返回 EOF

举个栗子:

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	FILE* pf = fopen("data.txt", "r");//以读的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	struct Stu n2 = { 0 };
	fscanf(pf,"%s %d %f" ,&n2.name ,&n2.age, &n2.score);
	printf("%s %d %f" ,n2.name, n2.age, n2.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

4.8、 fwrite

原型:

size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);

功能:用于将数据块写入 stream 指向的文件流中,是以二进制的形式写入的

ptr : 指向要写入的数据块

size : 要写入的每个数据项的大小

count : 要写入数据项的数量

stream : 指向要写入文件的流

返回:返回实际写入的数据项的数量

举个栗子:

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	FILE* pf = fopen("data.txt", "wb");//以写的形式打开文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	struct Stu n2 = { "zhangsan" ,18, 91.2};
	fwrite(&n2, sizeof(struct Stu), 1, pf);
	
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

只不过是以二进制写入的,所以要用二进制写入打开,同时,写入后的文件我们人类是看不懂的:

4.9、 fread

原型:

size_t fread(void* ptr, size_t size, size_t count, FILE* stream);

功能:从 stream 指向的文件流中以二进制的形式读取数据块,并将其存储到 ptr 指向的内存缓冲区中

返回:返回实际读取的数据项数量

与 fwrite 相对应的,这里就不多展示了

4.10、 sprintf 和 sscanf

4.10.1、 sprintf

原型 :

int sprintf(char* str, const char* format, ……);

功能:将格式化的数据写入字符数组(字符串)。简而言之,就是将格式化的数据转换为一个字符串

返回:成功,返回 str 的字符串数;

           失败,返回负数

举个栗子:

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct Stu n1 = { "matongtong" ,18 ,81.5 };
	char str[30];
	sprintf(str,"%s %d %f" ,n1.name, n1.age, n1.score);
	printf("%s" ,str);
	return 0;
}

结果:

4.10.2、 sscanf

原型:

int sscanf(const char* str, const char* format, …… );

功能:从字符串中读取格式化数据。常用于解析字符串中的结构化数据

返回:成功,返回解析并赋值的参数变量;

           失败,返回 EOF

举个栗子:

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct Stu n1 = { "matongtong" ,18 ,81.5 };
	char str[30];
	sprintf(str,"%s %d %f\n" ,n1.name, n1.age, n1.score);
	printf("%s" ,str);
	struct Stu n2 = { 0 };
	sscanf(str, "%s %d %f", n2.name, &n2.age, &n2.score);
	printf("%s %d %f" ,n2.name, n2.age, n2.score);
	return 0;
}

结果:

4.11scanf/printf 和 fscanf/fprintf 和 sscanf/sprintf

scanf / printf :针对标准输入/输出流的格式化输入/输入函数

fscanf / fprintf :针对所有输入/输出流的格式化输入/输入函数

sscanf / sprintf :从字符串中提取格式化数据 ;将格式化数据转化为字符

五、文件的随机读写

5.1、 fseek

原型:

int fseek( FILE* stream, long int offset,  int origin);

功能:根据文件指针的位置和偏移量来定位文件指针(光标)

offset :偏移量

origin :文件指针的起始位置,有三个常用的起始位置:

SEEK_SET  //文件开始的地方
SEEK_END  //文件末尾
SEEK_CUR  //文件指示器当前的位置

返回:定位成功,返回0;

           失败,返回非 0

举个栗子:

fseek(pf , 6, SEEK_SET);
fseek(pf , -5, SEEK_END);

然后在进行读/写操作

5.2、 ftell

原型:

long int ftell(FILE* stream);

功能:返回文件指针相对于起始位置的偏移量

例如:

int ret = ftell(pf);

5.3、rewind

原型:

void rewind(FILE* stream);

 功能:让文件指针回到文件起始位置

例如:

rewind(pf);

六、文件缓冲区

6.1、 fflush 

原型:

int fflush(FILE* stream);

功能:强制刷新 stream 指向的流的缓存区,确保数据写入到底层设备

返回:成功返回 0 ;失败返回 EOF

Note:

1、仅对输出流或更新流(最后一次操作位输出)有明确的刷新行为

2、输入流的刷新行为不可移植

3、程序正常终止或调用 fclose 时会自动刷新,但程序崩溃时缓冲区数据可能丢失

结语

恭喜你,通过本文的学习,相信你已经对C语言的文件操作有了一个全面的了解。文件操作是C语言编程中一项重要的技能,掌握它可以让你更好地处理数据,开发更实用的程序。

希望本文能够帮助你入门C语言的文件操作,并为你的程序开发之路添砖加瓦。在实际应用中,文件操作可能涉及到更复杂的情况,例如错误处理、文件格式的设计等。建议你多加练习,查阅相关资料,不断提升自己的技能。

记住,熟能生巧!只有通过不断的实践,才能真正掌握文件操作的精髓。祝你在C语言的世界里越走越远,写出更多精彩的代码!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值