如何使用C语言进行读写文件

这篇具有很好参考价值的文章主要介绍了如何使用C语言进行读写文件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

文章目录

前言

什么是文件

文件名

文件的打开与关闭

文件的打开与关闭

文件的顺序读写

字符读写

文本行的读写

格式化输入输出

二进制文件输入输出

文件的随机读写

 文件操作知识拓展

 文本文件和二进制文件

文件结束判定

 文件缓冲区

总结


前言

文件操作可能看起来很简单,但实际上它涉及到许多细节和技巧。在这篇博客中,我们将从基础开始,逐步深入,为您解析C语言中的文件操作


什么是文件

磁盘上的文件都是文件。

在程序设计中,我们一般谈的文件有两种:程序文件、数据文件

程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。 

数据文件

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

本期我们主要探讨数据文件

在以前处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件(从文件中读数据,将数据输出读入文件)。

文件名

在对文件进行操作时,我们必须要知道文件名,接下来我们先对文件名进行介绍。

 一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如: c:\code\test.txt

 为了方便起见,文件标识常被称为文件名。

文件的打开与关闭

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.

怎么理解呢?例如:

struct _iobuf {
    char *_ptr;
    int  _cnt;
    char *_base;
    int  _flag;
    int  _file;
    int  _charbuf;
    int  _bufsiz;
    char *_tmpfname;
   };
typedef struct _iobuf FILE;

 不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节

 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

 我们先来创建一个FILE* 的指针变量。

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

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

 或许大家有点决定抽象,那么接下来我们切身感受一下。

文件的打开与关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件

例如:

FILE * pf=fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );

 filename是要打开的文件名,  mode就是我们打开文件的方式。stream是指向文件的指针。

 打开方式如下:

文件使用方式 含义 如果指定文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 出错
“rb”(只读) 为了输入数据,打开一个二进制文件 出错
“wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件
“ab”(追加) 向一个二进制文件尾添加数据 出错
“r+”(读写) 为了读和写,打开一个文本文件 出错
“w+”(读写) 为了读和写,建议一个新的文件 建立一个新的文件
“a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 为了读和写打开一个二进制文件 出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写  建立一个新的文件

怎么用呢?

我们先来一个简单的,打开文件和关闭文件。

int main()
{
FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");//输出错误原因,双引号里为要检查的函数
		return 1;
	}
return 0;
}

 注意这里打开的方式是“r”,只读已经存在的文件,在程序当前文件中没有一个叫data类型为txt的文件,程序就会报错例如:

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 如果存在程序就不会有任何输出。

把“r”改成“w”,以写的形式打开,这时我们再次运行就会看到一个data.txt的文件。

我们可以右击代码标签页,转到所在文件夹:

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 就可以看到。如果看多到txt后缀可以选择打开文件扩展名:

点击查看,选择显示,勾选文件扩展名选项。

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 除此之外,我们还可以将文件放在其他路径下。

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

这样写叫相对路径。如果你想要创建在其他路径,这就要给出绝对路径,例如我们想把文件放在桌面上,这样也是可以的,只需知道路径就可以创建到桌面上,在需要创建的文件名前加上位置信息。 

例如:

FILE* pf = fopen("C:\\Users\\86150\\Desktop\\data.txt", "w");

 为了防止出现转义字符,可以将每个\后加一个\,只要知道想要存放的具体位置信息,就可以将文件创建到指定位置。

在相对路径的方法中,我们还可以将文件调整到上级目录的其他文件路径下,例如:

 点(.)是当前目录,(..)是上级目录,假设我们需要将文件放在程序上级目录的Debug文件下,我们就可以这样写:

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

 关闭文件就简单了,例如:

上述的方法我们打开文件,关闭文件就只需这样写:

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

关闭文件之后,及时将pf置为NULL。

文件的顺序读写

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

 说到流,这里我们进行简单科普:

读写文件时我们需要:

  • 打开文件
  • 读写文件
  • 关闭文件

 例如:我们在使用scanf,和printf时,并没有什么打开键盘,打开屏幕等一系列操作,默认就直接进行操作。

这是因为C程序只要运行起来,就默认打开三个流:

  • 标准输入流  stdin
  • 标准输出流  stdout
  • 标准错误流  stderr

所以在使用scanf,printf时就可以默认使用。 它们的类型都是FILE*类型。

什么是流?

流是指数据在计算机中的传输方式,它是数据的有序序列,可以是字节、字符、图像、音频或视频等形式输入流用于从外部读取数据到计算机内存中,而输出流则用于将计算机内存中的数据写入到外部设备或文件中。流的操作可以是顺序的,也可以是随机的。

我们继续回到文件读写函数。

fgetc是字符输入函数,一次读入一个字符到程序当中,fputc是字符输出函数,一次输出一个字符,它们都是适用于所有流的,可以是从键盘输入,也可以是从文件里读取输入。至于输出,可以输出到屏幕上,也可以输出到文件里。

字符读写

我们可以来尝试一下写文件:

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

 写26个字母写到这个data.txt文件中。

我们运行一下程序,在当前程序路径下找到data.txt文件。打开来看,它确实会按照顺序写入26个字母。 

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 如果我们想要输出到屏幕上,就只需把pf换成stdout就行了。fputc('a' + i, stdout);

 接下来我们尝试一下读文件:

我们将刚刚写入的文件数据保存,然后对文件进行读取数据:

int main()
{
	
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch;
	ch=fgetc(pf);
	printf("%c\n", ch);
	ch=fgetc(pf);int
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

这里fgetc的返回值是int类型,如果遇到文件末尾或者读取失败会返回EOF。

此外fgetc函数还可以从键盘上读取,只需改错stdin即可,ch=fgetc(stdin);

读取两次,读取两个字符输出到屏幕上,正常情况下运行应该是输出ab两个字符。文件在打开时,文件指针默认指向起始位置,当读完一个字符后,文件指针就会默认指向下一个位置。

或许大家会想一次读写一个字符太麻烦了。接下来就是文本行的读写。

文本行的读写

fgets和fputs

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

 这次我们重新读入,一次读入abcdef这6个字符。因为是同对一个文件进行操作,所有原先写入的数据会被覆盖。我们再次打开data.txt  

 就可以观察到,文件中只有新写入abcdef。

再来试一试读:

int main()
{
	
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch[20] = { 0 };
	fgets(ch, 20, pf);//读num-1个
	printf("%s\n", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

 fgets的参数依次是,读取后存放的位置,读取的字符个数(n-1)个,读取数据的位置。

 我们可以先在读数据的文件中输入长度超过20的字符进行验证。

格式化输入输出

接下来就是fprintf 和fscanf 

前边介绍的方法都是字符的读写,而fprintf 和fscanf是对数据进行格式读写,我们直接上代码:

struct S {
	int a;
	float s;
};
int main()
{

	FILE* pf = fopen("data.txt", "w");//注意,读写时操作不同,注意修改
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	struct S s = { 100,3.14f };
	fprintf(pf,"%d %f",s.a,s.s);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

 它可以将不同格式的数据输出到指定位置。和printf很类似,就是前边加上输出的位置。

 运行成功后,打开文件就可以看到100,3.140000出现在文件中。

 既然有格式化输出,那就必然有从文件中输入。

直接上代码:

struct S {
	int a;
	float s;
};
int main()
{

	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	struct S s = { 0 };
	fscanf(pf,"%d %f",&(s.a),&(s.s));
	printf("%d %f", s.a, s.s);
	fclose(pf);
	pf = NULL;
	return 0;
}

 我们可以保存之前的数据,再次运行,屏幕上就会输出文件中的数据。fscanf和我们经常适用的scanf也很相似,只是多了一个读取数据的位置。

除此之外还有sscanf和sprintf

我们可以来对比一下这几个函数:

  • scanf  从标准输入流读取格式化的数据。
  • printf  向标准输出流写格式化的数据。
  • fscanf  适用于所有输入流的格式化输入函数
  • fprintf  适用于所有输出流的格式化输出函数

sprintf  将格式化的数据写入到字符串中(将格式化的数据转化为字符串),废话不多讲我们直接上代码:

struct S {
	int a;
	float s;
	char str[20];
};
int main()
{
	char ch[30] = { 0 };
	struct S s = { 100,3.14f,"hello world" };
	sprintf(ch, "%d %f %s",s.a,s.s,s.str);
	return 0;
}

通过调试,观察ch中的数据:

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 可以观察到它确实将数据转化成了字符串,并存放到了数组当中。

和printf也很类似,只需在前边添加要存放的数组。

sprintf可以将任何类型的数据转换成字符串,那就会有还原的函数,那么这就是sscanf函数的作用。

struct S {
	int a;
	float s;
	char str[20];
};
int main()
{
	char ch[30] = { 0 };
	struct S s = { 100,3.14f,"hello world" };
	struct S t = { 0 };
	sprintf(ch, "%d %f %s",s.a,s.s,s.str);
	
	sscanf(ch, "%d %f %s", &(t.a),&(t.s), &(t.str));
	printf("%d %f %s\n", t.a,t.s,t.str);
	return 0;
}

sprintf将数据转换成字符串,sscanf将数据还原。通过结构体访问成员进而输出。

sscanf其实也是和scanf很像,它仅仅是在前边添加了一个转换的数组地址。

这里我们继续对文件操作函数进行介绍。

二进制文件输入输出

fread和fwrite

写文件:

struct S {
	int a;
	float s;
	char str[20];
};
int main()
{
	struct S s = { 100,12.56f,"hahaha" };
	FILE* pf = fopen("data.txt", "wb");//注意这里不再是w而实wb以二进制形式写入。
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&s,sizeof(struct S),1,pf);写一个结构体数据到文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

 创建一个结构体s并进行初始化。fwrite怎么用呢?它的第一个参数是数据的来源,第二个参数是数据的大小,第三个参数是写入的个数,第四个参数是写入的位置。

运行成功后,此时打开文件data.txt观察数据:

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 结果是乱码,这就是二进制文件,但这并不是谁都读不懂,计算机也可以将数据转换成正常数据。这就是接下来要介绍的fread函数的功能

我们看代码:

struct S {
	int a;
	float s;
	char str[20];
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("data.txt", "rb");//注意这里打开文件的方式需要修改rb
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fread(&s, sizeof(struct S), 1, pf);
	printf("%d %f %s", s.a, s.s, s.str);
	fclose(pf);
	pf = NULL;
	return 0;
}

 它和fwrite函数传递的参数一致,但函数作用恰好相反。运行程序后就可以将原先写入的二进制文件转变为正常数据。

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

文件的随机读写

前边我们都是在写文件的顺序读写,其实文件还存在随机读写。关于随机读写的函数有3种

fseek、ftell和rewind

fseek

根据文件指针的位置和偏移量来定位文件指针。

 ftell

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

rewind

让文件指针的位置回到文件的起始位置

 我们直接看应用:

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 9, SEEK_SET);//文件指针偏移  SEEK_SET初始位置开始偏移
	int ch=fgetc(pf);        //SEEK_CUR  当前位置开始偏移
	printf("%c\n", ch);      //SEEK_END  末尾位置开始偏移

	int p=ftell(pf);//计算文件内容偏移量
	printf("%d\n", p);

	rewind(pf);//回到起始位置
	ch = fgetc(pf);
	printf("%c\n", ch);

	fclose(pf);
	pf = NULL;
	return 0;
}

 fseek第一个参数是文件指针,定位要读取的文件,第二个参数是偏移量,第三个参数是文件起始位置。

上述程序运行前可以在文件中初始化为26个字母。运行就可以看到一下结果:

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他

 文件操作知识拓展

 文本文件和二进制文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件

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

 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

 直白点说就是,打开一个文件,我们能看懂的就属于文本文件,看不懂的字符就是二进制文件。

一个数据在内存中是怎么存储的呢?

 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储

 例如:整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。

文件结束判定

被错误使用的 feof

切记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 

当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束

 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets)

  • fgetc判断是否为EOF.
  • fgets判断返回值是否为NULL.

二进制文件的读取结束判断,判断返回值是否小于实际要读的个数 

  • fread判断返回值是否小于实际要读的个数

这里就不再举例介绍了,这些细节上的问题一定要注意。

 文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定

c语言读文件,C语言进阶,c语言,开发语言,经验分享,其他


 总结

本期内容到此结束,在本篇博客中,向大家介绍了C语言中的文件操作技巧,帮助您更好地管理和处理文件。无论您是初学者还是有经验的程序员,这些技巧都能够为您带来便利和效率。希望您能够在日后的工作和学习中不断运用和完善这些技能!最后,感谢阅读!文章来源地址https://www.toymoban.com/news/detail-723080.html

到了这里,关于如何使用C语言进行读写文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Prompt工程师指南[从基础到进阶篇]:用于开发和优化提示,以有效地使用语言模型(LMs)进行各种应用和研究主题

    Prompt工程是一种相对较新的学科,用于开发和优化提示,以有效地使用语言模型(LMs)进行各种应用和研究主题。Prompt工程技能有助于更好地理解大型语言模型(LLMs)的能力和局限性。研究人员使用Prompt工程来改善LLMs在各种常见和复杂任务上的能力, Prompt engineering(提示工程

    2024年02月04日
    浏览(62)
  • 【头歌】——数据分析与实践-基于Python语言的文件与文件夹管理-文本 文件处理-利用csv模块进行csv文件的读写操作

    第1关 创建子文件夹 第2关 删除带有只读属性的文件 第3关 批量复制文件夹中的所有文件 未通过本题,如果您通过了本题欢迎补充到评论区,有时间我会整理进来 第1关 读取宋词文件,根据词人建立多个文件 第2关 读取宋词文件,并根据词人建立多个文件夹 第3关 读取宋词文

    2024年01月25日
    浏览(73)
  • Qt项目开发经验:在Linux平台下使用Qt进行开发

    Qt项目开发经验:在Linux平台下使用Qt进行开发 如今,Qt已成为跨平台应用程序开发中的一大宠儿。在Linux平台下,Qt的应用也是越来越广泛了。今天,我将和大家分享一些我在Linux平台下使用Qt进行开发的经验。 首先,在Linux平台下安装Qt并不复杂。我们可以通过apt-get工具来安

    2024年02月08日
    浏览(65)
  • QT-如何使用RS232进行读写通讯

    以下是一个使用Qt进行RS232通讯的具体示例,包括读取和写入数据的操作: 在这个示例中,我们定义了一个全局的 QSerialPort 对象 serial 用于串口通讯。首先设置串口名称和波特率,并打开串口。通过连接 readyRead 信号到 readData 槽函数来读取串口数据。 readData 函数读取串口数据

    2024年02月13日
    浏览(50)
  • 【经验分享】如何使用VSCode对比两个文件

    当有两个不同版本的文件,如何使用VSCode对比两个文件 长按 ctrl 选择想要对比的两个文件-----右键选择 将已选项进行比较 ----大功告成 大功告成

    2024年02月07日
    浏览(62)
  • 【vim进阶】vim编辑器的多文件操作(如何打开多个文件,如何进行文件间的切换,如何关闭其中的某一个文件)

    现在有多个文件 file1 ,file2 , … ,filen. 现在举例打开两个文件 file1,file2 该方式打开文件,显示屏默认显示第一个文件也就是 file1。 在命令模式下使用edit指令 打开一个文件可以通过: edit filename(或者简写为 e ) 。这里的文件名可以采用绝对路径或者是相对路径。 例如我们想打

    2024年02月16日
    浏览(49)
  • Android应用程序中使用 Gemini Pro AI开发——2年工作经验如何淘汰10年工作经验的Android开发?

    上周,谷歌推出了最强大的基础模型 Gemini 。 Gemini 是多模式的AI——它可以接受文本和图像输入。 谷歌为 Android 开发者引入了一种在设备上,利用最小模型Gemini Nano的方法。此功能可通过 AICore 在部分设备上使用,这是一项处理模型管理、运行时、安全功能等的系统服务,可

    2024年01月18日
    浏览(68)
  • 【C语言】一下让你懂得如何进行文件操作

    各位csdn的朋友大家好呀,在学习C语言的过程中,我们可以通过写不同的代码来存放不同的数据。在程序运行时,数据是存放在内存当中的,但是当程序关闭时,数据自然就消失了。所以今天我要和大家分享的是如何将数据存放在文件中的知识哦 ! 我们前面学习结构体时,写

    2023年04月16日
    浏览(44)
  • 使用Go语言进行安卓开发

    本文将介绍如何使用Go语言进行安卓开发。我们将探讨使用Go语言进行安卓开发的优点、准备工作、基本概念和示例代码。通过本文的学习,你将了解如何使用Go语言构建高效的安卓应用程序。 随着移动互联网的快速发展,安卓应用程序的需求越来越旺盛。使用传统的Java和K

    2024年02月06日
    浏览(46)
  • 【C语言进阶】智能管理:如何使用柔性数组实现内存优化

    目录 一、定义 二、用法 三、特点 四、注意事项 五、总结 在 C 语言中,柔性数组(Flexible Array)是一种特殊类型的数组,它允许程序员在运行时动态地分配数组的大小,从而实现更灵活的内存管理。本文将详细介绍柔性数组的定义、用法、特点及注意事项。 C 语言的数组是

    2023年04月24日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包