首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >文件操作知识

文件操作知识

作者头像
承渊政道
发布2025-12-18 17:11:41
发布2025-12-18 17:11:41
1870
举报

1. 为什么使⽤⽂件?

如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤⽂件.

2. 什么是⽂件?

磁盘(硬盘)上的⽂件是⽂件.

但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件、数据⽂件(从⽂件功能的⻆度来分类的)。

2.1 程序⽂件

程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows

环境后缀为.exe).

2.2 数据⽂件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件.

本篇讨论的是数据⽂件.

在以前所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到显⽰器上.

其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件.

2.3 ⽂件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤.

⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀

例如:c:\code\test.txt .为了⽅便起⻅,⽂件标识常被称为⽂件名.

3. ⼆进制⽂件和⽂本⽂件

根据数据的组织形式,数据⽂件被称为⽂本⽂件和⼆进制⽂件.

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

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

⼀个数据在⽂件中是怎么存储的呢?

字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储.

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式输出,则在磁盘上只占4个字节.

代码语言:javascript
复制
#include <stdio.h>
 int main()
 {
 int a = 10000;
 FILE* pf = fopen("test.txt", "wb");
 fwrite(&a, 4, 1, pf);
 fclose(pf);
 pf = NULL;
 return 0;
 }

4. ⽂件的打开和关闭

4.1 流和标准流
4.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,抽象出了流的概念,我们可以把流想象成流淌着字符的河.C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的.⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作.

4.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?

那是因为C语⾔程序在启动的时候,默认打开了3个流:

1️⃣stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据.

2️⃣stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出

流中.

3️⃣stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯.

这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的.

stdin、stdout、stderr 三个流的类型是:FILE * ,通常称为⽂件指针.

C语⾔中,就是通过FILE* 的⽂件指针来维护流的各种操作的.

4.2 ⽂件指针

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

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

例如,VS2013 编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:

代码语言:javascript
复制
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*的指针变量:

代码语言:javascript
复制
FILE* pf;//⽂件指针变量

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

4.3 ⽂件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件.

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系.

ANSIC规定使⽤fopen函数来打开⽂件,fclose 来关闭⽂件.

代码语言:javascript
复制
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );

mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

文件使用方式

含义

如果指定文件不存在

"r"(只读)

为了输入数据,打开了一个已经存在的文本文件

出错

"w"(只写)

为了输出数据,打开一个文本文件

建立一个新的文件

"a"(追加)

向文本文件尾添加数据

建立一个新的文件

"rb"(只读)

为了输入数据,打开一个二进制文件

出错

"wb"(只写)

为了输入数据,打开一个二进制文件

建立一个新的文件

"ab"(追加)

向一个二进制文件尾添加数据

建立一个新的文件

"r+"(读写)

为了读和写,打开一个文本文件

出错

"w+"(读写)

为了读和写,建议一个新的文件

建立一个新的文件

"a+"(读写)

打开了一个文件,在文件尾进行读写

建立一个新的文件

"rb+"(读写)

为了读和写打开一个二进制文件

出错

"wb"(读写)

为了读和写,新建一个新的二进制文件

建立一个新的文件

"ab+"(读写)

打开一个二进制文件,在文件尾进行读和写

建立一个新的文件

代码语言:javascript
复制
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","w");
if (pFile!=NULL)
{
fputs ("fopen example",pFile);
fclose (pFile);
}
return 0;
}

5. ⽂件的顺序读写

5.1 顺序读写函数介绍

函数名

功能

适用于

fgetc

字符输入函数

所有输入流

fputc

字符输出函数

所有输出流

fgets

文本行输入函数

所有输入流

fputs

文本行输出函数

所有输出流

fscanf

格式化输出函数

所有输入流

fprintf

格式化输出函数

所有输出流

fread

二进制输入

文本输入流

fwrite

二进制输出

文本输出流

代码语言:javascript
复制
#include <stdio.h>
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
     int ch = fgetc(pf);
     printf("%c\n", ch);//a
     ch = fgetc(pf);
     printf("%c\n", ch);//b
     ch = fgetc(pf);
     printf("%c\n", ch);//c
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}
//读取字符成功,返回字符的ASCII的值.如果读取失败或者遇到文件末尾
//设置一个错误状态值-ferror;如果遇到文件末尾,设置一个遇到文件末尾的状态值-feof
代码语言:javascript
复制
#include <stdio.h>
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char arr[20] = "hello";
	int num = 100;
	double pai = 3.14;
	fprintf(pf, "%s %d %.2lf", arr, num, pai);	
	fclose(pf);
	pf = NULL;
	return 0;
}
代码语言:javascript
复制
#include <stdio.h>
struct S
{
	char arr[20];
	int num;
	double pai;
};
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
	perror("fopen");
		return 1;
	}
	struct S s = { "world", 200, 3.14 };	
	fprintf(pf, "%s %d %.2lf", s.arr, s.num, s.pai);
	fclose(pf);
	pf = NULL;
    return 0;
}

上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流).

5.2 对⽐⼀组函数:

scanf/fscanf/sscanf

printf/fprintf/sprintf

1️⃣scanf(输入)/printf(输出):针对标准输入(stdin-键盘)输出(stdout-屏幕)的格式化的输入/输出函数

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

3️⃣sscanf/sprintf:从一个字符中提取格式化的数据把格式化的数据转换成字符串.

6. ⽂件的随机读写

6.1 fseek

根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标).

代码语言:javascript
复制
int fseek ( FILE * stream, long int offset, int origin );
代码语言:javascript
复制
#include <stdio.h>
 int main ()
 {
 FILE * pFile;
 pFile = fopen ( "example.txt" , "wb" );
 fputs ( "This is an apple." , pFile );
 fseek ( pFile , 9 , SEEK_SET );
 fputs ( " sam" , pFile );
 fclose ( pFile );
 return 0;
 }
6.2 ftell

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

代码语言:javascript
复制
long int ftell ( FILE * stream );
代码语言:javascript
复制
#include <stdio.h>
 int main ()
 {
 FILE * pFile;
 long size;
 pFile = fopen ("myfile.txt","rb");
 if (pFile==NULL)
 perror ("Error opening file");
 else
 {
 fseek (pFile, 0, SEEK_END); 
 size=ftell (pFile);
 fclose (pFile);
 printf ("Size of myfile.txt: %ld bytes.\n",size);
 }
 return 0;
 }
6.3 rewind

让⽂件指针的位置回到⽂件的起始位置.

代码语言:javascript
复制
void rewind ( FILE * stream );
代码语言:javascript
复制
#include <stdio.h>
 int main ()
 {
 int n;
 FILE * pFile;
 char buffer [27];
 pFile = fopen ("myfile.txt","w+");
 for ( n='A' ; n<='Z' ; n++)
 fputc ( n, pFile);
 rewind (pFile);
 fread (buffer,1,26,pFile);
 fclose (pFile);
 buffer[26]='\0';
 printf(buffer);
 return 0;
 }

7. ⽂件读取结束的判定

7.1 被错误使⽤的 feof

牢记:在⽂件读取过程中,不能⽤ feof 函数的返回值直接来判断⽂件的是否结束.

feof 的作⽤是:当⽂件读取结束的时候,判断读取结束的原因是否是:遇到⽂件尾结束.

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

例如:

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

2️⃣⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。

例如:

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

代码语言:javascript
复制
//⽂本⽂件的例⼦
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,⾮char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 
while ((c = fgetc(fp)) != EOF) 
{
putchar(c);
}
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
代码语言:javascript
复制
 //⼆进制⽂件的例⼦
 #include <stdio.h>
 enum { SIZE = 5 };
 int main(void)
 {
 double a[SIZE] = {1.,2.,3.,4.,5.};
 FILE *fp = fopen("test.bin", "wb");
 fwrite(a, sizeof *a, SIZE, fp); 
 fclose(fp);
 double b[SIZE];
 fp = fopen("test.bin","rb");
 size_t ret_code = fread(b, sizeof *b, SIZE, fp);
 if(ret_code == SIZE) {
 puts("Array read successfully, contents: ");
 for(int n = 0; n < SIZE; ++n)
 printf("%f ", b[n]);
 putchar('\n');
 } else { 
 if (feof(fp))
 printf("Error reading test.bin: unexpected end of file\n");
 else if (ferror(fp)) {
 perror("Error reading test.bin");
 }
 }
 fclose(fp);
 }

8. ⽂件缓冲区

代码语言:javascript
复制
 #include <stdio.h>
 #include <windows.h>
 int main()
 {
 FILE*pf = fopen("test.txt", "w");
 fputs("abcdef", pf);//先将代码放在输出缓冲区
 printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
 Sleep(10000);
 printf("刷新缓冲区\n");
 fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
 //注:fflush 在⾼版本的VS上不能使⽤了
 printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
 Sleep(10000);
 fclose(pf);
 //注:fclose在关闭⽂件的时候,也会刷新缓冲区
 pf = NULL;
 return 0;
 }

以上就是今天的博客内容了,希望能够帮助到读者朋友们!

我们一起继续加油努力💪!一键三连🙏!!!

本篇完结,点赞收藏加关注,找到小编不迷路,感谢大家🙏🤝!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 为什么使⽤⽂件?
  • 2. 什么是⽂件?
    • 2.1 程序⽂件
    • 2.2 数据⽂件
    • 2.3 ⽂件名
  • 3. ⼆进制⽂件和⽂本⽂件
  • 4. ⽂件的打开和关闭
    • 4.1 流和标准流
    • 4.1.1 流
    • 4.1.2 标准流
    • 4.2 ⽂件指针
    • 4.3 ⽂件的打开和关闭
  • 5. ⽂件的顺序读写
    • 5.1 顺序读写函数介绍
    • 5.2 对⽐⼀组函数:
  • 6. ⽂件的随机读写
    • 6.1 fseek
    • 6.2 ftell
    • 6.3 rewind
  • 7. ⽂件读取结束的判定
    • 7.1 被错误使⽤的 feof
  • 8. ⽂件缓冲区
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档