💬 欢迎讨论:在阅读过程中有任何疑问,欢迎在评论区留言,我们一起交流学习!
👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏,并分享给更多对C语言感兴趣的朋友
文章目录
文件,究竟是什么鬼?
说到文件,大部分人可能会想:“哎呀,不就是那种看得见摸得着的东西嘛!”没错,文件就是磁盘上的一堆信息,只不过它们的样子不太一样。比如有程序文件(扩展名 .c
,.exe
什么的)和数据文件(它们存储的是你在程序里使用的数据)。别看这些文件表面平凡,它们的背后可是大有文章。
我们常见的文件有两大类:
- 程序文件:这些通常是你的源代码文件(像
.c
后缀的文件),以及编译后产生的目标文件(例如.obj
或.exe
)。这类文件代表的是程序的执行逻辑和操作。 - 数据文件:这些文件是程序运行过程中需要的外部数据,或者是程序执行后的输出结果。
文件本身并不复杂,了解了这些,你就能轻松搞定文件操作。
文件指针——你的文件“朋友”
文件操作不可能不提文件指针。你和文件的每次互动,都得有个“桥梁”——这就是文件指针。它就像是你和文件沟通的一个小助手,通过它,你可以告诉文件“嘿,快给我读点东西”,或者“你可以关机啦”。不信?来看看下面这段代码:
FILE* fp = fopen("myfile.txt", "w");
这个 fp
变量就是文件指针,它让你能够连接到文件 myfile.txt
,然后对它进行各种操作。想关上它吗?那就用 fclose
把它“请走”:
fclose(fp);
fp = NULL; // 防止野指针乱来
通过文件指针,你可以在内存中找到文件的相关信息(如文件名、文件状态等),也可以方便地进行读取或写入操作。
文件打开的那些“门道”
打开文件有一堆方式,比如只读、只写、追加等等。就像你去餐厅点菜,有的人喜欢点个“只读的沙拉”,有的人则想要一份“只写的牛排”。不过,要记住,一旦选定了“门道”,就不能乱点其他的了!
打开文件的方式有很多种,主要有以下几种模式:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输入数据,打开一个文本文件 | 建立一个新文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新文件 |
”ab“ | 向一个二进制文件尾添加数据 | 出错 |
例如,你想打开一个文件并向其中写入内容,可以使用以下代码:
#include <stdio.h>
int main ()
{
FILE * pFile;
//打开文件
pFile = fopen ("myfile.txt","w");
//文件操作
if (pFile != NULL)
{
fputs ("fopen example", pFile);
//关闭文件
fclose (pFile);
}
return 0;
}
常用的顺序输入输出函数——让你有条不紊的使用文件
-
字符输入函数(fgetc)
- 函数名称:
fgetc
- 适用流:所有输入流
- 功能:
fgetc
用于从文件中读取一个字符。无论是从标准输入流(如键盘)还是文件流,它都可以读取字符。
- 函数名称:
-
字符输出函数(fputc)
- 函数名称:
fputc
- 适用流:所有输出流
- 功能:
fputc
用于将一个字符写入文件,或者输出到其他流(如标准输出流)。
- 函数名称:
-
文本行输入函数(fgets)
- 函数名称:
fgets
- 适用流:所有输入流
- 功能:
fgets
用于从文件中读取一行文本,通常用于处理每行数据时,比fgetc
更加方便,因为它能读取整行数据。
- 函数名称:
-
文本行输出函数(fputs)
- 函数名称:
fputs
- 适用流:所有输出流
- 功能:
fputs
将一行文本写入文件或者输出流,适合写入字符串。
- 函数名称:
-
格式化输入函数(fscanf)
- 函数名称:
fscanf
- 适用流:所有输入流
- 功能:
fscanf
是一个格式化输入函数,类似于scanf
,用于从文件中读取格式化的数据(如整数、浮点数、字符串等)。
- 函数名称:
-
格式化输出函数(fprintf)
- 函数名称:
fprintf
- 适用流:所有输出流
- 功能:
fprintf
是一个格式化输出函数,类似于printf
,可以将格式化的输出写入文件或其他流。
- 函数名称:
-
二进制输入(fread)
- 函数名称:
fread
- 适用流:文件
- 功能:
fread
用于从文件读取二进制数据,这与从文件读取文本数据不同,适用于处理二进制文件。
- 函数名称:
-
二进制输出(fwrite)
- 函数名称:
fwrite
- 适用流:文件
- 功能:
fwrite
用于将二进制数据写入文件,常用于文件格式为二进制的应用。
- 函数名称:
流的含义——数据的“大马路”
图中的流指的是数据流向的方向,简而言之,流就像一个通道,通过它,程序可以与不同的数据源(如文件、标准输入/输出、网络)进行数据交换。
- 标准输入流(stdin):表示从键盘或者其他输入设备获取数据。类型为
FILE*
。 - 标准输出流(stdout):表示向屏幕或终端输出数据,也常用于程序的输出。类型为
FILE*
。 - 标准错误流(stderr):专门用来输出错误信息,通常输出到屏幕,帮助调试和记录错误。类型为
FILE*
。
这些流在 C 语言中有默认的设置,你可以使用它们来与程序外部的数据进行交互。
fseek和ftell——让你随意“跳跃”文件
如果文件指针是你和文件之间的“通信员”,那 fseek
和 ftell
就是你用来“调皮捣蛋”的工具。想要快速跳转到文件的某个位置?fseek
就是你的“跳跃王者”。它能让你精准地告诉文件:“嘿!快点,跳到那个地方!”
fseek(fp, 100, SEEK_SET); // 跳到文件的第100个字节
如果你是个数字控,ftell
会告诉你文件指针现在在第几字节,完美配合使用,你就能像个文件专家一样随心所欲地翻阅文件。
long int position = ftell(fp);
printf("Current position: %ld\n", position);
有了它们,文件就像一张无穷无尽的地图,而你是那个能够自由穿梭的探险家。
rewind——倒带重放,穿越回文件的“起点”
偶尔我们会在文件中“迷失”,对吧?但别担心,rewind
函数会帮你找回“方向感”,它能把文件指针迅速拉回文件的起点,仿佛你按下了“倒带”按钮,所有操作都能重新开始。
rewind(fp); // 把文件指针拉回到开头
是不是感觉像是时光机?你可以重新阅读文件的内容,或者再次执行某些操作,完全不需要重开一个新文件。
文件缓冲区——它的幕后“神操作”
当你和文件在“亲密互动”的时候,文件并不总是立刻回应你。为什么?因为文件背后有一个隐秘的“缓冲区”,这个缓冲区可不是随便的东西,它像是个内存仓库,存储着所有待操作的数据。系统会先把数据传递到缓冲区,再从缓冲区读取或写入磁盘。你可能觉得它不够直接,但这也让整个过程更高效。
不过,既然是“幕后工作”,它就不喜欢被打扰。如果你不小心忽略了这个缓冲区的刷新(比如直接关闭文件),数据可能就会丢失。所以,记得及时调用 fflush
来刷新缓冲区,确保文件内容没跑掉:
fflush(fp); // 刷新缓冲区,确保数据写入文件
文件读取结束的判定——别让它“赖账”
当你和文件打交道时,总会遇到“结束”的问题。你是不是也曾经遇到过文件读到一半就突然消失的情况?别怕,文件结束有专门的信号,比如 EOF
(文件结束符)或者 NULL
(读取错误)。当你遇到这些信号时,表示文件已经结束或发生了错误,赶紧收拾收拾,不要继续“纠缠”下去。
文本文件的读取可以使用 fgetc
来判断是否遇到 EOF
,或者用 fgets
判断返回值是否为 NULL
,这都能告诉你文件是否正常结束。
对于二进制文件,我们则需要注意 fread
的返回值,它能告诉你是否成功读取了预期的字节数。
#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 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
总结:文件不再神秘
文件操作的世界其实并没有你想象的那么复杂。只要掌握了文件打开、读写、关闭的基本技巧,再加上对 fseek
和 rewind
这些“小工具”的灵活运用,你就能轻松自如地管理你的文件了。
所以,下次当你面对文件时,不要再紧张了!只要记得好好地与它互动,文件也会变成你忠实的小伙伴,陪你一起完成各种任务,轻松搞定所有挑战。