C语言文件操作入门:fopen、fclose、fputc、fgetc、fputs、fgets、fprintf、fscanf、fwrite、fread详解

article/2025/10/11 1:53:04

本篇文章主要讲解4组函数:

  1. 字符读写:fputc和fgetc。
  2. 文本行读写:fputs和fgets。
  3. 格式化读写:fprintf和fscanf。
  4. 二进制读写:fwrite和fread。

顺序读写文件

预备知识:fopen和fclose

如果我们要读写一个文件,就必须先打开这个文件,读写完后,还需要关闭这个文件。这就像,你要喝一杯水,需要先打开杯盖,才能喝水,喝完水后还需要把盖子盖上。

打开文件的原理是,打开文件后,在内存中创建一个FILE类型的变量,用来记录打开的文件的相关信息。FILE类型是一个结构体类型。

关闭文件的原理是,根据这个FILE类型的变量里描述的文件信息,通过一定手段把文件关闭。

在学习C语言的过程中,我们不需要知道具体的细节,会用就行了。C语言中打开文件需要使用函数fopen,关闭文件需要使用函数fclose。

fopen的声明如下:

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

看这个声明,可以了解到,第一个参数就是要打开文件的文件名,第二个参数是用什么方式打开(读?写?还是其他模式?)。函数会打开这个文件,在内存中创建一个相对应的文件信息区,其实就是创建一个FILE类型的变量,这个变量记录了文件的相关信息。接着,这个函数会返回这个FILE变量的地址,如果函数打开文件失败会返回NULL指针。

这里为了简单起见,都在工程目录下操作文件,所以文件名不用带上路径。如果要在其他位置操作文件,根据具体情况带上绝对路径或者相对路径就可以了。

假设我们要操作的文件的文件名是test.txt,我们想要写这个文件(写文件的模式是"w",及write的简写),可以这么调用这个函数:

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

这里的2个参数都用双引号引起,因为是字符串。返回值需要使用一个FILE*的指针来接收。和malloc类似,需要检查返回值是否为NULL指针,如果为NULL指针,则打开文件失败,需要进行错误处理,举个例子:

if (pf == NULL)
{perror("fopen");exit(1);
}

以上的代码中,当检查到pf为NULL,此时打开文件失败,用perror报个错,再exit掉,终止进程。

当然,操作文件不只有"w"一种模式。本篇博客主要介绍比较常见的4种模式,分别是:

  1. “w” - 写文件,即write的简写。
  2. “r” - 读文件,即read的简写。
  3. “wb” - 通过二进制的方式写文件,b是binary的缩写。
  4. “rb” - 通过二进制的方式读文件。

fclose函数的声明如下:

int fclose(FILE * stream);

具体的使用很简单,前面我们用一个FILE*的指针来接收fopen函数的返回值,只需要把这个指针传给fclose就能关闭对应的文件了。和free函数类似,fclose函数没有能力把传给它的指针置为NULL,为了防止野指针,需要程序员手动置为NULL值。

fclose(pf);
pf = NULL;

1.字符读写:fputc和fgetc

fputc用于向文件中写入一个字符。

读写文件前,应该打开文件。这次以"w"的模式打开。

FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{perror("fopen");exit(1);
}

接下来是使用fputc写文件的操作。写完文件后,需要关闭文件。

fclose(pf);
pf = NULL;

后面的函数都是按照打开文件->读写文件->关闭文件的顺序,唯一的区别是打开文件的方式不一样,也就是fopen的第二个参数不一样。

fputc的声明如下:

int fputc(int character, FILE* stream);

很明显,第一个参数表示写入的字符,第二个参数表示指向文件信息区的文件指针。比如,如果我要把字符’a’写到pf对应的文件里,应该这么写:

fputc('a', pf);

举一反三:如果要把小写的a~z,总共26个字母写到文件中,应该如何写呢?

for (int ch = 'a'; ch <= 'z'; ch++)
{fputc(ch, pf);
}

程序执行的结果是:创建了一个test.txt文件,并写入了a~z。
运行结果
接下来,我们来读一下这个文件。使用fgetc之前也需要打开文件,打开的模式是"r",使用完后需要关闭文件。

fgetc是用来读取一个字符的。声明如下:

int fgetc(FILE* stream);

函数会读一个字符然后返回这个字符的ASCII码值。如果读取失败会返回EOF。

如果想读取刚刚写完的文件,把26个字母读出来,可以使用循环读26次,但是如果不知道文件中有多少字符呢?那就一直读取,直到读完为止。那如何判断读取结束呢?当fgetc返回EOF的时候读取就结束了。

int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{printf("%c ", ch);
}

运行结果如下:
运行结果

2.文本行读写:fputs和fgets

使用fputs之前,要以"w"的模式打开文件,使用完后要关闭文件,操作同上。

fputs是用来写入文本行的,声明如下:

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

str表示要写入的字符串。比如,我写10行Hello, world!!!进去,每写一个就换个行。

for (int i = 0; i < 10; i++)
{fputs("Hello, world!!!\n", pf);
}

可以发现,test.txt中就有了10行Hello, world!!!。
运行结果
想把它们读出来,可以使用fgets,打开文件的模式是"r"。声明如下:

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

str表示你想把读到的字符串存在哪,num表示存储的空间有多大(最多存多少个字符,包括字符串结尾的’\0’)。函数会把str返回回来,如果读取失败,会返回NULL指针。所以读取时,可以使用循环,通过每次判断返回值是否为NULL指针,来判断是否读取结束。

char str[256] = {0};
while (fgets(str, 256, pf) != NULL)
{puts(str);
}

输出结果:
输出结果
由于写入时每个Hello, world!!!后面都写了个’\n’,而puts会在打印完字符串后也换个行,所以相当于每次打印完Hello, world!!!后面都换2次行。如果你只想换一次行,可以使用printf来实现:

char str[256] = { 0 };
while (fgets(str, 256, pf) != NULL)
{printf("%s", str);
}

输出结果如下:
输出结果

3.格式化读写:fprintf和fscanf

fprintf和fscanf,与printf和scanf非常像,唯一的区别就是,fprintf和fscanf前面多了个f。(emmm,听君一席话,如听一席话)

在学习这两个函数之前,建议先学一下sprintf和sscanf这两个函数,点这里

我还是采取同样的讲解思路。你也许不知道fprintf和sscanf,但你一定知道printf和scanf(别告诉我你不知道)。

先说printf。举个例子,假设有一个结构体:

struct S
{int i;double d;char arr[30];
};

我创建了一个结构体变量:

struct S s = {10, 3.14, "abcdef"};

我想你把这个结构体的数据用printf打印到屏幕上,你会怎么写?

printf("%d %lf %s\n", s.i, s.d, s.arr);

如果这些数据不是打印到屏幕上,而是“打印”到文件中,只需要在函数名前面加个f,所有参数最前面加个pf,就行了。

fprintf(pf, "%d %lf %s\n", s.i, s.d, s.arr);

简单吧?注意fprintf需要的打开文件方式是"w",使用完后需要关闭文件。来看看此时的test.txt文件:
输出结果
干得漂亮!接下来来看看fscanf。fscanf使用前需要使用"r"模式打开文件,使用完后需要关闭文件,都学到这了,别忘了!(稍稍总结一下,目前所有写文件操作打开文件的模式都是"w",即write的简写,而读文件操作打开文件的模式都是"r",即read的缩写。)

还是那句话,你也许没有听说过fscanf,但你一定直到scanf。假设我创建了一个结构体变量:

struct S s = {0};

我想你用scanf函数,实现从键盘中输入数据到s中,你会怎么写?

scanf("%d %lf %s", &s.i, &s.d, s.arr);

如果不是从键盘中输入数据,而是从文件中读取数据,只需要在函数名前加个f,参数最前面加个pf就行了。

fscanf(pf, "%d %lf %s", &s.i, &s.d, s.arr);

读完之后,我们可以把s中的数据打印出来。

printf("%d %lf %s\n", s.i, s.d, s.arr);

看吧,读取成功了。
输出结果

4.二进制读写:fwrite和fread

前面我们都是读写字符文件,也就是以字符的形式读写文件,这是我们能看的懂的形式。但是在计算机的世界中,都是二进制的,所以我们还需要学习用二进制的方式来读写文件。

先来学习下fwrite。由于是二进制的形式读写文件,打开文件的模式是"wb",b代表二进制。函数的声明如下:

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

fwrite函数和fread函数的参数列表是一样的,所以学会一个就相当于两个都学会了。第一个参数表示你要写入的数据在内存中存储的位置,第二个参数表示写入一个数据的大小,第三个参数表示要写入几个数据。

比如,我有一个结构体变量:

struct S s = {10, 3.14, "abcdef"};

我想把它以二进制的形式写到内存中,第一个参数就是s的地址,第二个参数就是一个结构体的大小,由于我只想写1个s进去,所以第三个参数就是1。

fwrite(&s, sizeof(s), 1, pf);

输出结果:
输出结果
看不懂呀!很正常,因为这玩意是二进制。接下来我们用二进制的方式来把数据重新读出来。

fread是用来二进制的读文件的,打开文件的模式是"rb",使用完后需要关闭文件。函数的声明如下:

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

第一个参数表示要把读到的数据存到哪,第二个参数表示读取一个数据的大小,第三个参数表示要读几个数据。

比如,我们先创建一个结构体:

struct S s = {0};

接着把读到的数据放到s里,有了前面fwrite的经验,写起来就简单了。

fread(&s, sizeof(s), 1, pf);

接着把s中的数据打印出来:

printf("%d %lf %s\n", s.i, s.d, s.arr);

输出结果如下:
输出结果

5.格局打开

其实,我们可以把“屏幕”和“键盘”也当成文件。用stdout表示屏幕,stdin表示键盘,举个例子:

fprintf(stdout, "%d %lf %s\n", s.i, s.d, s.arr);

这行代码会把结构体s中的数据写入到stdout这个“文件”中,而stdout表示屏幕,所以也就把结构体中的数据打印到屏幕上了!哈哈哈,有意思吧。

其实,stdout是标准输出流,而stdin是标准输入流。在前面的函数中,只要是以"w"模式打开文件才能使用的函数(fputc, fputs, fprintf)的FILE*类型的参数就可以传stdout,而以"r"模式打开文件才能使用的函数(fgetc, fgets, fscanf)的FILE*类型的参数就可以传stdin,分别表示“写屏幕”(把数据打印到屏幕上)和“读键盘”(从键盘中输入数据)。

你可能纳闷了,为啥stdout和stdin就不用有fopen和fclose这样的操作呢?这是因为,只要一个C程序跑起来,就默认打开了三个流,分别是:stdout(标准输出流),stdin(标准输入流)和stderr(标准错误流),所以不需要我们手动打开。

这时,你再想想,printf和fprintf有什么区别?区别就是,printf只能格式化输出标准输出流的数据,而fprintf可以格式化输出任意输出流的数据(包括标准输出流和文件流);同理,scanf和fscanf的区别是,scanf只能格式化输入标准输入流中的数据,而fscanf可以输入任意输入流的数据(包括标准输入流和文件流)。那sprintf和sscanf和它们之间有什么区别呢?这两个函数是用来进行格式化数据和字符串的相互转换的,就跟这些“流”没什么关系了。

总结

  1. 操作文件前,需要使用fopen打开文件,注意fopen和malloc类似,需要判断返回值。fopen的2个参数分别是文件名(可以带上路径)和打开文件的方式(本篇文章主要讲解了"w", “r”, “wb”, "rb"四种方式)。返回类型是FILE*类型的指针。
  2. 操作文件结束后,需要使用fclose关闭文件,注意fclose和free类似,关闭完文件后建议把文件指针置为NULL值,防止出现野指针。
  3. 使用"w"模式打开文件后,fputc可以把一个字符写入到文件中,例子:fputc('a', pf);
  4. 使用"r"模式打开文件后,fgetc可以从文件中读取一个字符,例子:ch = fgetc(pf);
  5. 使用"w"模式打开文件后,fputs可以往文件中写入一个文本行,例子:fputs("abcde", pf);
  6. 使用"r"模式打开文件后,fgets可以从文件中读取一个文本行,例子:fgets(str, 256, pf);
  7. 使用"w"模式打开文件后,fprintf可以往文件中写入格式化数据,如果不知道怎么使用,可以先想成用printf把格式化数据打印到屏幕上,然后把函数名改成fprintf,参数最前面加上pf,就是把格式化数据“打印”到文件中了。例子:fprintf(pf, "%d\n", i); // i为一个int类型的变量
  8. 使用"r"模式打开文件后,fscanf可以从文件中读取格式化数据,如果不知道怎么使用,可以先想成用scanf从键盘中输入数据,然后把函数名改成fscanf,参数最前面加上pf,就是从文件中输入格式化数据了。例子:fscanf(pf, "%d", &i); // i为一个int类型的变量
  9. 使用"wb"模式打开文件后,fwrite可以以二进制的形式往文件中写入数据;使用"rb"模式打开文件后,fread可以以二进制的方式从文件中读取数据,它们的参数都是类似的,为:void* ptr, size_t size, size_t count, FILE* stream,都需要指定内存中数据的位置,一个数据的大小,操作几个数据以及操作哪个文件。
  10. 格局打开,前面说的“文件”本质上是“文件流”,除了文件流之外,还有标准输入流(stdin),标准输出流(stdout),标准错误流(stderr)等,而所谓的“写入到文件”其实是“写入到文件流”中,如果把参数中的pf改成stdout,就是“写入到标准输出流”中,其实就把数据打印到屏幕上了。同理,“从文件中读取”本质上是“从文件流中读取”,如果把pf改成stdin,就是“从标准输入流中读取”,其实就是从键盘中读取。
  11. stdin, stdout, stderr这三个流不需要手动打开,一个C程序开始运行时,会自动打开这三个流。

感谢大家的阅读!


http://chatgpt.dhexx.cn/article/Bi1Dbe0H.shtml

相关文章

linux fputc fgetc fseek rewind 函数

目录 前言fputc 函数fgetc 函数fseek函数rewind函数 前言 <sprintf fprintf 函数 > 前一节讲了 sprintf fprintf 函数 的缓存问题 在他们的基础上加了一个while&#xff08;1&#xff09;&#xff1b; 来验证 结果都是输出的 不管怎么样 都会写入到内核态 内核态在输出…

c语言中fputc函数的作用是,C语言中fputc函数的用法

C语言中fputc函数的用法 C语言中fputc函数的用法为“int fgetc (FILE *fp)”&#xff0c;该函数的作用是从指定的文件中读取一个字符&#xff0c; 读取成功时会返回读取到的字符&#xff0c;读取到文件末尾或读取失败时返回EOF。推荐教程&#xff1a;《C语言》 示例代码#includ…

STM32重写fputc

操作步骤 01、在项目中&#xff0c;加上以下函数即可&#xff08;作用&#xff1a;重写fputc&#xff09; 库函数版 int fputc( int ch, FILE *f ){ USART_SendData(USART1,(u8) ch ); while(USART_GetFlagStatus(USART1,USART_F…

STM32重写fputc汇总

1. 在工程项目中加上函数fputc 例如&#xff1a; 库函数版 int fputc( int ch, FILE *f ) {USART_SendData(USART2,(u8) ch );while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)RESET);return ch; }寄存器版 int fputc(int ch, FILE *f){ while((USART2->SR&0X40)0)…

Keil 重定向 fputc 函数 以及 printf 函数的代码尺寸测试

本文的开发环境为 Keil Cortex-M3 内核处理器。 重定向 fputc 函数方法 如果想使用库函数 printf &#xff0c;必须要将 fputc 重定向到自己的串口上。 术语 重定向 可以理解为用户重写 fputc 函数&#xff0c;在重写的函数体内调用自己硬件的串口发送函数。 在 Keil 环境中…

fputc函数

/****************************************功能&#xff1a;写一个字节到文件流中*参数&#xff1a;* c 写入的字符* stream 流指针*返回值&#xff1a;* 成功返回字符* 失败返回EOF或errno*************************************/ int fputc(int c, FILE *stre…

标准c库:fputc,fgetc,feof

1、了解标准c库fopen等用法&#xff08;点击跳转&#xff09; 2、fputc写一个字符到文件 #include <stdio.h>int main() {FILE *fp;fp fopen("./test.txt","w");//int fputc(int c, FILE *stream);fputc(a,fp);fclose(fp);return 0; }3、fputc写一…

文件的输入输出函数

文章目录 前言字符输入输出函数 - fgetc和fputc文本输入输出函数 - fgets和fputs格式化输入输出函数 - fscanf和fprintf二进制输入输出函数 - fread和fwrite 前言 在文件操作函数&#xff08;一&#xff09;中&#xff0c;我们已经学会了怎样正确地打开和关闭一个文件&#xf…

计算机网络 | 传输层的两个重要协议——TCP、UDP

目录 传输层概述 传输层的作用 运输层端口号、复用与分用的概念 发送方的复用和接收方的分用 UDP和TCP的对比 TCP的流量控制 TCP的拥塞控制 TCP的差错控制 UDP的差错控制的和流控 TCP的封装格式 TCP主要特点 TCP的连接 可靠传输的工作原理 TCP连接的三次握手和四次挥…

以太网协议

以太网(Ethernet) 以太网是一种局域网技术,其规定了访问控制方法、传输控制协议、网络拓扑结构、传输速率等,完成数据链路层和物理层的一些内容,它采用一种称作CSMA/CD的媒体接入方法,其意思是带冲突检测的载波侦听多路接入(Carrier Sense, Multiple Access with Collisi…

传输层TCP协议和UDP协议

传输层协议 文章目录 传输层协议传输层&#xff1a;传输层协议&#xff1a;UDP协议&#xff1a;TCP协议&#xff1a;UDP协议和TCP协议的比较&#xff1a;适用场景&#xff1a; 传输层&#xff1a; 传输层是OSI中最重要&#xff0c;最关键的一层,是唯一负责总体的数据传输和数据…

文件传输协议FTP

文件传输协议FTP(File Transfer Protocol)是因特网中使用最广泛的文件传输协议。FTP使用交互式的访问&#xff0c;允许客户指定文件的类型和格式(如指明是否使用ASCII码)&#xff0c;并允许文件具有存取权限(如访问文件的用户必须经过授权&#xff0c;并输入有效的口令)。 文件…

传输层协议详解

一、传输层的概念和服务 1、传输层的基本概念 传输层负责端到端之间的数据传输控制传输层依赖于网络层的服务&#xff0c;对应用层提供传输服务 2、传输层的功能 跟踪会话 跟踪源主机和目的主机上应用程序间的每次通信 数据分段 将数据分段&#xff0c;并管理每个分段 …

TCP-面向连接的传输层协议

TCP 主要特点工作方式建立连接---三次握手为什么 TCP 建立连接需要三次握手&#xff0c;而不是两次&#xff1f;连接终止---四次挥手为什么要四次挥手为什么要等待2MSL TCP流量控制TCP拥塞控制1.慢开始和拥塞避免2.快重传和快恢复 传输控制协议&#xff08;TCP&#xff0c;Tran…

简介 传输层协议——UDP协议

UDP协议&#xff1a; UDP&#xff1a;User Datagram Protocol 用户数据报协议 UDP简介&#xff1a; UDP是一种面向无连接的传输层协议&#xff0c;UDP数据包括目的端口号和源端口号信息&#xff0c;由于通讯不需要连接&#xff0c;所以可以实现广播发送。UDP提供面向事务的简…

TCP(传输控制协议详解)详解

TCP协议的特点 &#xff08;1&#xff09;TCP协议是面向连接的运输层协议 在数据传输前必须建立连接&#xff0c;数据传输之后释放连接。 &#xff08;2&#xff09;TCP提供可靠交付的服务 所谓可靠是指在传输过程中无重复&#xff0c;无丢失&#xff0c;无错误。但是同时会…

TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)

TCP/IP协议 TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff0c;传输控制协议/网际协议&#xff09;是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议&#xff0c;而是指一个由FTP、SMTP、TCP、UDP、IP等协…

用户数据报协议UDP和传输控制协议TCP

目录 UDP UDP的功能 UDP的特点 UDP的首部格式 TCP TCP的功能 TCP的特点 TCP面向流的概念 套接字&#xff08;socket) 无连接工作流程和面向连接的工作流程对比 UDP UDP的功能 UDP只在IP的数据报服务上增加了很少一点功能&#xff1a; 1.复用和分用功能 2.差错检测…

TCP(传输控制协议)

TCP 最主要的特点 TCP 是应用程序在使用 TCP 协议之前&#xff0c;必须先建立 TCP 连接。 在传送数据完毕后&#xff0c;必须释放已经建立的 TCP 连接面向连接的运输层协议每一条 TCP 连接只能有两个&#xff0c;每一条 TCP 连接只能是(一对一)点对点TCP 提供的服务。 通过 TCP…

传输层-传输控制协议(TCP)

目录 TCP 协议概述TCP 报文段结构TCP连接管理三次握手四次挥手 TCP 可靠数据传输TCP拥塞控制 TCP 协议概述 传输控制协议&#xff08;TCP&#xff09;是Internet传输层协议。提供面向连接、可靠、有序、字节流传输服务。 面向连接&#xff1a;应用程序在使用 TCP 之前&#x…