C语言——文件操作_学习笔记

一、引言——为什么使用文件

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

C语言文件操作的意义在于实现数据的持久化,以便于数据的读取、存储和处理,为程序设计提供方便。程序的数据和各种外部设备之间是怎么联系起来的,这里涉及到“流”的概念。

二、流和标准流

2.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。

C程序针对文件、画面、键盘等的数据输入输出操作都是同流操作的。

一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

2.2 标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:

  • stdin - 标准输入流,在大多数的环境中从键盘输入。
  • stdout - 标准输出流,大多数的环境中输出至显示器界面。
  • stderr - 标准错误流,大多数环境中输出到显示器界面。

就是因为默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。stdin、stdout、stderr三个流的类型是:FILE*,通常称为文件指针。
C语言中,就是通过FILE*的文件指针来维护流的各种操作的。

三、文件指针

在C语言中,文件指针用于在程序中操作文件。一个文件指针是一个特殊的结构体变量,每个被使用的文件都在内存中开辟了一个相应的文件信息区,这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。文件指针用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等。在C语言中,所有的输入/输出操作都是通过文件指针完成的。

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

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

FILE* pf;//创建一个⽂件指针变量

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

四、文件的打开和关闭

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

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

ANSIC规定使用fopen函数来打开文件,fclose来关闭文件。

  • fopen和fclose函数原型
//打开文件
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+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件
  • 实例代码举例
#include <stdio.h>int main() {FILE *file;// 使用 fopen 函数打开文件file = fopen("test.txt", "w");if (file == NULL) {printf("无法打开文件\n");return 1;}// 向文件写入内容fprintf(file, "Hello, World!");// 使用 fclose 函数关闭文件fclose(file);return 0;
}

在这个例子中,我们首先使用 fopen 函数以写入模式 (“w”) 打开一个名为 “test.txt” 的文件。如果文件无法打开(例如,由于权限问题或文件已存在但不可写),fopen 将返回 NULL,我们在这种情况下打印一条错误消息并返回 1 以指示程序出错。

然后,我们使用 fprintf 函数向文件写入一条消息。在这里,fprintf 的第一个参数是文件指针,第二个参数是格式化字符串,类似于 printf 函数。

最后,需要使用 fclose 函数关闭文件。这是很重要的,因为如果你忘记关闭文件,可能会导致数据丢失或其他不可预知的问题。在大多数情况下,你应该始终确保在完成对文件的操作后关闭它。

五、文件的顺序读写

5.1 顺序读写函数介绍

函数名功能适用于
fgetc字符输入函数(将文件中的数据输入到内存中)所有输入流
fputc字符输出函数所有输出流
fgets文本行输入函数所有输入流
fputs文本行输出函数所有输出流
fscanf格式化输入函数所有输入流
fprintf格式化输出函数所有输出流
fread二进制输入文件
fwrite二进制输出文件

5.2 函数应用举例

1.fgetc和fputc函数

fgetc(一次读取文件中的一个字符到内存中)

int fgetc ( FILE * stream );//fgetc函数原型
#include <stdio.h>int main(){FILE* file = fopen("data.txt", "r");  // 打开名为 "data.txt" 的文件以供读取if (file == NULL) {printf("无法打开文件\n");return 1;}char ch;while ((ch = fgetc(file)) != EOF) // 读取文件直到遇到文件结束符(EOF){ printf("%c", ch);  // 打印每个读取的字符}fclose(file);  // 关闭文件return 0;
}

上面的例子中,把data.txt文件中的所有字符内容打印在屏幕上。
data,txt 事先编辑好内容,文件内容如下:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

fputc(一次把内存中的一个字符写入到文件中)

int fputc ( int character, FILE * stream );//fputc函数原型
#include<stdio.h>int main()
{FILE* fp = fopen("data.txt","w");char a[] = "hello bit~~";for (int i = 0; i < 10; i++){fputc(a[i], fp);}fclose(fp);return 0;
}

代码中使用了 fputc 函数将字符串 “hello bit~~” 的前10个字符写入到 “data.txt” 文件中。
在这里插入图片描述

2.fputs和fgets函数

fputs函数一次向一个文件写入一行字符串。

int fputs ( const char * str, FILE * stream );//fputs函数原型
#include <stdio.h>int main() 
{FILE* file = fopen("example.txt", "w");  // 打开一个文件以写入if (file == NULL) {printf("无法打开文件\n");return 1;}const char* text = "Hello, World!\n";  // 这是我们将要写入的字符串fputs(text, file);  // 将字符串写入到 file 指向的文件fclose(file);  // 关闭文件return 0;
}

运行代码后,代码路径下生成example.txt文件内容如下:
在这里插入图片描述
在上述代码中,fputs 函数接收两个参数:要写入的字符串和一个文件指针。这个函数将字符串写入到文件,然后我们使用 fclose 来关闭文件。

fgets函数一次从一个文件中读取一行字符串。

char * fgets ( char * str, int num, FILE * stream );//fgets函数原型

接下来,我们使用 fgets 来从同一个文件中读取这行字符串:

#include <stdio.h>int main(){FILE *file = fopen("example.txt", "r");  // 打开一个文件以读取if (file == NULL) {printf("无法打开文件\n");return 1;}char buffer[100];  // 创建一个缓冲区来保存文件中的字符串fgets(buffer, sizeof(buffer), file);  // 从 file 指向的文件读取字符串到 buffer 中printf("%s", buffer);  // 打印读取到的字符串fclose(file);  // 关闭文件return 0;
}

运行结果如下:
在这里插入图片描述

在这个代码中,fgets 函数接收三个参数:一个目标缓冲区,缓冲区的大小,以及一个文件指针。fgets 将从文件中读取最多大小为缓冲区大小的字符串,并保存到缓冲区中。然后我们使用 printf 来打印读取到的字符串,最后我们再次使用 fclose 来关闭文件。

3.fscanf和fprintf函数

格式化输入输出函数

int fscanf ( FILE * stream, const char * format, ... );//fscanf函数原型
int fprintf ( FILE * stream, const char * format, ... );//fprintf函数原型
//对比scanf和printf
int scanf ( const char * format, ... );
int printf ( const char * format, ... );

通过对以上函数原型的对比可以知道:fscanf和fprintf函数这俩个函数使用十分类似,fscanf和fprintf是多了一个文件指针参数,其他的都和scanf、printf一样。直接看例子:

#include <stdio.h>int main() {FILE *file;file = fopen("test.txt", "w");if (file == NULL) {printf("无法打开文件\n");return 1;}int i;for (i = 0; i < 10; i++) {fprintf(file, "数字 %d\n", i);}fclose(file);return 0;
}

在这个例子中,我们使用 fopen 打开一个名为 “test.txt” 的文件以写入数据。然后使用 fprintf 将一些数字写入到这个文件中。最后,我们使用 fclose 来关闭文件。

然后,我们来看一下 fprintf 的例子:

#include <stdio.h>int main() {FILE *file;file = fopen("test.txt", "r");if (file == NULL) {printf("无法打开文件\n");return 1;}char buffer[100];while (fscanf(file, "%s", buffer) != EOF) {printf("%s\n", buffer);}fclose(file);return 0;
}

在这个例子中,我们使用 fopen 打开一个名为 “test.txt” 的文件以读取数据。然后使用 fscanf 从这个文件中读取字符串,并将其打印出来。当 fscanf 读取到文件结束符(EOF)时,循环终止。最后,我们使用 fclose 来关闭文件。

4. fread和fwrite函数

freadfwrite是C语言中的函数,它们用于从文件中读取和写入数据。

fread函数用于从文件中读取数据,其语法如下:

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

其中,ptr是指向要读取数据的缓冲区的指针,size是每个元素的大小,count是要读取的元素个数,stream是文件流指针。

fwrite函数用于将数据写入文件中,其语法如下:

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

其中,ptr是指向要写入文件的数据的指针,size是每个元素的大小,count是要写入的元素个数,stream是文件流指针。

这两个函数通常用于二进制文件的读写。
在这里插入图片描述
在读写时,必须使用二进制模式,需要注意文件的打开方式(如只读wb、只写rb、追加ab等)以及文件指针的位置。


这里有一个小知识点freadfwrite是有返回值的,freadfwrite的返回值是size_t类型的整数值,表示函数成功读取或写入的元素个数。

正常读取或写入操作情况下,返回值都应该是函数参数中包含的元素个数,即等于size_t count

如果读取或写入操作失败,fread和fwrite将返回一个比预期小的值。例如,如果你试图读取10个元素,但只读取了5个,那么fread将返回5。同样,如果你试图写入10个元素,但只写入了5个,那么fwrite将返回5。

你可以使用fread和fwrite的返回值来检查是否成功读取或写入了预期数量的元素。如果返回值与预期不符,则可能需要处理错误或采取其他措施。


举例
假设你有一个名为“input.txt”的文本文件,其内容如下所示:

This is an example.
It shows how to use fread and fwrite.

下面是一个C程序,它读取“input.txt”文件中的内容,将其写入一个新文件“output.txt”中,并输出成功读取和写入的元素个数:

#include <stdio.h>int main() {FILE *input_file, *output_file;char buffer[1024];size_t bytes_read, bytes_written;// 打开输入文件input_file = fopen("input.txt", "rb");if (input_file == NULL) {fprintf(stderr, "无法打开输入文件!\n");return 1;}// 打开输出文件output_file = fopen("output.txt", "wb");if (output_file == NULL) {fprintf(stderr, "无法打开输出文件!\n");fclose(input_file);return 1;}// 使用fread和fwrite进行读写操作while ((bytes_read = fread(buffer, sizeof(char), sizeof(buffer), input_file)) != 0) {bytes_written = fwrite(buffer, sizeof(char), bytes_read, output_file);printf("已读取 %zu 个元素,已写入 %zu 个元素\n", bytes_read, bytes_written);}// 关闭文件fclose(input_file);fclose(output_file);printf("读取和写入完成!\n");return 0;
}

运行该程序后,它将输出以下内容:

已读取 27 个元素,已写入 27 个元素
已读取 27 个元素,已写入 27 个元素
读取和写入完成!

六、文件的随机读写

在C语言中,可以使用以下函数来实现文件的随机读写:

函数名用途
fseek()用于设置文件指针的位置。可以使用的文件指针位置模式包括SEEK_SET(从文件开头算起)、SEEK_CUR(从当前位置算起)和SEEK_END(从文件末尾算起)。
ftell()用于获取当前文件指针的位置。
rewind()让文件指针的位置回到文件的起始位置
fread()用于从文件中读取数据。可以指定读取的元素大小和数量,以及文件指针的位置。
fwrite()用于将数据写入文件中。可以指定要写入的数据大小和数量,以及文件指针的位置。

6.1 fseek

int fseek ( FILE * stream, long int offset, int origin );
int fseek(FILE *stream, long int offset, int origin )
参数:
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
offset -- 这是相对 whence 的偏移量,以字节为单位。
origin -- 参照点,可以是 SEEK_SET(从文件开始位置开始移动),SEEK_CUR(从当前位置开始移动),或 SEEK_END(从文件末尾开始移动)。返回值:
如果成功,则该函数返回零,否则返回非零值。

6.2 ftell

long int ftell ( FILE * stream );
long int ftell(FILE *stream)
参数
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。返回值
ftell 函数的返回值是当前文件指针的位置偏移量,以字节为单位,从文件的开头开始计算。

6.3 rewind

void rewind(FILE *stream);//函数原型//代码举例↓↓↓↓
/* rewind example */
#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);//等价于printf("%s",buffer);return 0;
}

以上代码的运行结果如下:
在这里插入图片描述
以上代码的意思是:

首先使用一个循环,用fputc把大写字母’A’到大写字母‘Z’这26个字符写入到名为’myfile.txt’的文件中,写完之后,这时文件指针指向的位置是文件的末尾,使用rewind()函数,让文件指针的位置回到文件的起始位置,接着再使用fread()函数从头到尾把文件的内容读取到buffer数组中,然后给数组buffer最后一个元素设置为\0,(加上字符串的结束标志),最后使用printf函数,以字符串的形式打印buffer数组中的内容。


6.4 代码举例

使用这些函数可以实现文件的随机读写,例如:

FILE *fp;
char buffer[100];
int num;fp = fopen("file.txt", "r");  // 打开文件
if(fp == NULL) {printf("Error opening file\n");return -1;
}fseek(fp, 10, SEEK_SET);  // 将文件指针移动到第10个字节处
num = fread(buffer, sizeof(char), 20, fp);  // 从当前位置读取20个字节,存储到buffer数组中
fclose(fp);  // 关闭文件

上述代码中,首先使用fopen()函数打开文件,然后将文件指针移动到第10个字节处,从当前位置读取20个字节并将其存储到buffer数组中,最后使用fclose()函数关闭文件。

七、文件读取结束的判定

C语言文件操作过程中,避免不了对文件结束的判定,特别是读取文件数据到内存中去的时候,不可能让程序一直读取文件中的内容不停止,而是当文件中所有内容都读到内存中后,结束文件读取的操作。

7.1 feof

关于文件结束的判定,首先要了解一个函数feof

int feof ( FILE * stream );//函数原型

该函数的返回值有两种可能:
如果文件读取操作结束不是 因为文件已经到达末尾,feof() 函数返回 0,即 false。
如果文件读取操作结束是因为文件已经到达末尾,feof() 函数返回非零值,即 true。

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

feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。

以下是使用feof()函数的基本例子:

#include <stdio.h>int main() 
{FILE *file = fopen("test.txt", "r");if (file == NULL) {printf("Failed to open file\n");return 1;}char ch;while (!feof(file)) {ch = fgetc(file);if (ch != EOF) {printf("%c", ch);}}fclose(file);return 0;
}

在这个例子中,我们使用fgetc()函数一次读取一个字符,然后使用feof()检查是否到达文件末尾。如果feof(file)返回非零值,就意味着文件正常读取结束,文件指针已经到达文件末尾,所以循环停止。

需要注意的是,即使在最后一次读取之后,feof()仍然会返回非零值,因为它只是在读取操作之前检查文件状态。换句话说,如果你在最后一次读取操作之后再次调用feof(),它仍然会返回非零值。如果你想在最后一次读取操作之后确认文件已经读取结束,你需要使用ferror()函数来检查是否有其他错误发生。

7.2 fgets()或者getc()

另一种判定文件是否已经读取到结束的方法是使用fgets()或getc()等读取函数读取文件,然后检查结果是否为EOF。例如:

#include <stdio.h>int main() 
{FILE *file = fopen("test.txt", "r");if (file == NULL){printf("Failed to open file\n");return 1;}char buffer[100];while (fgets(buffer, 100, file) != NULL){printf("%s", buffer);}fclose(file);return 0;
}

在这个例子中,我们使用fgets()函数一次读取一行,当fgets()返回NULL时,意味着已经到达文件末尾。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/130175.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring Boot】日志文件

日志文件 一. 日志文件有什么用二. 日志怎么用三. ⾃定义⽇志打印1. 在程序中得到⽇志对象2. 使⽤⽇志对象打印⽇志3. ⽇志格式说明 四. 日志级别1. ⽇志级别有什么⽤2. ⽇志级别的分类与使⽤ 五. 日志持久化六. 更简单的⽇志输出—lombok1. 添加 lombok 依赖2. 输出⽇志3. lom…

VueRouter与expres/koa中间件的关联

ueRouter: runQueue 路由守卫都是有三个参数to,from,next。其中next就是下方的fn执行时候传入的第二个参数(回调函数)&#xff0c;只有该回调执行后才会挨个遍历queue内的守卫。 中间件的作用 隔离基础设施与业务逻辑之间的细节。详细的内容位于《深入浅出Node.js》P210 另外一…

深度学习-卷积神经网络-AlexNET

文章目录 前言1.不同卷积神经网络模型的精度2.不同神经网络概述3.卷积神经网络-单通道4.卷积神经网络-多通道5.池化层6.全连接层7.网络架构8.Relu激活函数 1.LeNet-52.AlexNet1.架构2.局部响应归一化&#xff08;VGG中取消了&#xff09;3.重叠/不重叠池化4.过拟合-数据增强5.过…

Scala第二十章节

Scala第二十章节 scala总目录 文档资料下载 章节目标 理解Akka并发编程框架简介掌握Akka入门案例掌握Akka定时任务代码实现掌握两个进程间通信的案例掌握简易版spark通信框架案例 1. Akka并发编程框架简介 1.1 Akka概述 Akka是一个用于构建高并发、分布式和可扩展的基于事…

jvm--对象实例化及直接内存

文章目录 1. 创建对象2. 对象内存布局3. 对象的访问定位4. 直接内存&#xff08;Direct Memory&#xff09; 1. 创建对象 创建对象的方式&#xff1a; new最常见的方式、Xxx 的静态方法&#xff08;单例模式&#xff09;&#xff0c;XxxBuilder/XxxFactory 的静态方法Class 的…

Linux 部署 MinIO 分布式对象存储 配置为 typora 图床

前言 MinIO 是一款高性能的对象存储系统&#xff0c;它可以用于大规模的 AI/ML、数据湖和数据库工作负载。它的 API 与Amazon S3 云存储服务完全兼容&#xff0c;可以在任何云或本地基础设施上运行。MinIO 是开源软件&#xff0c;也提供商业许可和支持 MinIO 的特点有&#x…

【力扣每日一题】2023.10.10 移动机器人

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目比较复杂&#xff0c;我概括一下。给我们一个数组表示不同机器人在一维坐标轴上的初始位置&#xff0c;还有一个字符串表示每个机器人…

Netty(四)NIO-优化与源码

Netty优化与源码 1. 优化 1.1 扩展序列化算法 序列化&#xff0c;反序列化主要用于消息正文的转换。 序列化&#xff1a;将java对象转为要传输对象(byte[]或json&#xff0c;最终都是byte[]) 反序列化&#xff1a;将正文还原成java对象。 //java自带的序列化 // 反序列化 b…

黑马点评-07缓存击穿问题(热点key失效)及解决方案,互斥锁和设置逻辑过期时间

缓存击穿问题(热点key失效) 缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且重建缓存业务较复杂的key突然失效了,此时无数的请求访问会在瞬间打到数据库,带来巨大的冲击 一件秒杀中的商品的key突然失效了&#xff0c;由于大家都在疯狂抢购那么这个瞬间就会有无数的请求…

小谈设计模式(19)—备忘录模式

小谈设计模式&#xff08;19&#xff09;—备忘录模式 专栏介绍专栏地址专栏介绍 备忘录模式主要角色发起人&#xff08;Originator&#xff09;备忘录&#xff08;Memento&#xff09;管理者&#xff08;Caretaker&#xff09; 应用场景结构实现步骤Java程序实现首先&#xff…

TLR4-IN-C34-C2-COO,一种结合了TLR4抑制剂TLR4-IN-C34的连接器

TLR4-IN-C34-C2-COO是一种结合了TLR4抑制剂TLR4-IN-C34的连接器&#xff0c;在免疫调节中发挥重要作用&#xff0c;它通过抑制TLR4信号通路的传导&#xff0c;从而达到降低炎症反应的目的。TLR4是Toll样受体家族中的一员&#xff0c;它主要识别来自细菌和病毒的保守模式&#x…

vue2踩坑之项目:Swiper轮播图使用

首先安装swiper插件 npm i swiper5 安装出现错误&#xff1a;npm ERR npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: vue/eslint-config-standard6.1.0 npm ERR! Found: eslint-plugin-vue8.7.1 npm ERR! node_modules/esl…