【C语言】文件操作,文件读写详细介绍

news2025/2/24 0:47:18

目录

为什么要使用文件?

文件概念

1. 什么是文件?

2. 程序文件

3. 数据文件

4. 文件名

文件的使用

1. 文件指针

2. 文件的打开与关闭

文件的顺序读写 

1. 顺序读写函数

2. scanf系列与printf系列

文件的随机读写 

1. fseek

2. ftell

3. rewind

文本文件,二进制文件 

文件读取结束的判定 

文件缓冲区


为什么要使用文件?

我们前面学习结构体时,写通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。
我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。
这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件或存放到数据库等方式。
使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。


文件概念

1. 什么是文件?

1. 文件是指磁盘上的文件。

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

3. 但是在程序设计中,我们一般谈的文件有两种,是从文件功能的角度来分类的:程序文件、数据文件。

2. 程序文件

1. 源文件(后缀为.c)。

2. 目标文件(windows环境下后缀为.obj)。

3. 可执行程序(windows环境下后缀为.exe)。

3. 数据文件

程序运行需要从中读取数据的文件,或者输出内容的文件。

4. 文件名

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

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

3. 文件名包含3部分:文件路径+文件名主干+文件后缀,例如: c:\code\ + test + .txt


文件的使用

1. 文件指针

1. 文件指针是指文件类型的指针。

2. 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。

3. 这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。

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

typedef struct _iobuf FILE;

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

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

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

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

2. 文件的打开与关闭

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

打开文件

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

关闭文件

int fclose(FILE* stream);

2. 打开模式(mode)如下:

.

3. 例子

int main ()
{
    FILE* pFile;
    //打开文件
    pFile = fopen("myfile.txt","w");
    //文件操作
    if (pFile!=NULL)
    {
        fputs("fopen example",pFile);
        //关闭文件
        fclose(pFile);
    }
    return 0;
}

文件的顺序读写 

1. 顺序读写函数

.

1. 将一个字符写入流中。

int fputc(int character, FILE* stream);

//例子
int main ()
{
    FILE* pFile = fopen("myfile.txt","w");
    fputc('a', pFile);  
    fclose(pFile);
    
    return 0;
}

.

2. 从流中获取一个字符。

int fgetc(FILE* stream);

//例子
int main ()
{
    FILE* pFile = fopen("myfile.txt","r");
    int ch = fgetc(pFile);  
    printf("%c\n", ch);
    fclose(pFile);
    
    return 0;
}

读的原理:文件打开,文件指针默认指向起始位置,读一个之后文件指针就往后一个位置。

.

3. 读与写的理解

这里我们的主语(主视角)始终是程序。

.

4. 将字符串写入流

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

//例子
int main()
{
    FILE* pFile = fopen("myfile.txt", "w");
    fputs("abc\n", pFile);
    fclose(pFile);
    pFile = NULL;

    return 0;
}

.

5. 从流中获取字符串

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

读num-1个,或者遇到换行,或者到文件末尾停下。

int main()
{
    FILE* pFile = fopen("myfile.txt", "r");
    char str[10];
    fgets(str, 10, pFile);
    printf("%s\n", str);
    fclose(pFile);
    pFile = NULL;

    return 0;
}

.

6. 将格式化数据写入流

int fprintf(FILE* stream, const char* format, ...);

例子

struct S
{
    int i;
    float f;
}s = {1, 3.1f};

int main()
{
    FILE* pFile = fopen("myfile.txt", "w");
    
    fprintf(pFile, "%d, %f", s.i, s.f);

    fclose(pFile);
    pFile = NULL;

    return 0;
}

.

7. 从流中读取格式化数据

int fscanf(FILE* stream, const char* format, ...);

例子

struct S
{
    int i;
    float f;
}s = { 0 };

int main()
{
    FILE* pFile = fopen("myfile.txt", "r");

    fscanf(pFile, "%d, %f", &(s.i), &(s.f));
    printf("%d, %f", s.i, s.f);

    fclose(pFile);
    pFile = NULL;

    return 0;
}

.

8. 将数据块写入流(二进制输出)

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

从 ptr 指向的空间开始拷贝 count 个大小为 size 的数据到 stream 流中。

struct S
{
    int i;
    float f;
};

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

    struct S s = { 1, 1.0 };
    fwrite(&s, sizeof(s), 1, pf);

    fclose(pf);
    pf = NULL;

    return 0;
}

因为是二进制文件,所以用文本文件打开就会乱码。

.

9. 从流中读取数据块(二进制输入)

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

从 stream 流中读 count 个大小为 size 的数据放到 ptr 所指向的空间。 

struct S
{
    int i;
    float f;
};

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

    struct S s;
    fread(&s, sizeof(s), 1, pf);
    printf("%d, %f\n", s.i, s.f);

    fclose(pf);
    pf = NULL;

    return 0;
}

2. scanf系列与printf系列

printf系列

1. 将格式化数据打印到 stdout

int printf(const char* format, ...);

2. 将格式化数据写入流

int fprintf(FILE* stream, const char* format, ...);

3. 将格式化数据写入字符串

int sprintf(char* str, const char* format, ...);

scanf系列

1. 从 stdin 读取格式化数据

int scanf(const char* format, ...);

2. 从流中读取格式化数据

int fscanf(FILE* stream, const char* format, ...);

3. 从字符串中读取格式化数据

int sscanf(const char* s, const char* format, ...);

sprintf与sscanf的例子

struct S
{
    int i;
    float f;
};

int main()
{
    char arr[20];
    struct S s = { 1, 1.0 };
    sprintf(arr, "%d, %f", s.i, s.f);
    printf("arr:%s\n", arr);
    struct S tmp;
    sscanf(arr, "%d, %f", &(tmp.i), &(tmp.f));
    printf("tmp:%d, %f\n", tmp.i, tmp.f);

    return 0;
}


文件的随机读写 

1. fseek

int fseek(FILE* stream, long int offset, int origin);

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

2. 控制 stream 文件指针从 origin 开始偏移 offset。 

3. 文件指针默认在文件起始位置,origin有三个选项控制文件指针的起始位置。

例子

文件指针默认指向起始位置也就a,我想访问c。

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

    fseek(pf, -2, SEEK_END); 
    printf("%c\n", fgetc(pf));

    fclose(pf);
    pf = NULL;
}

2. ftell

long int ftell(FILE* stream);

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

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

    printf("%c\n", fgetc(pf));
    printf("%c\n", fgetc(pf));
    printf("%c\n", fgetc(pf));
    printf("%d\n", ftell(pf));

    fclose(pf);
    pf = NULL;
}

3. rewind

void rewind(FILE* stream);

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

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

    printf("%c\n", fgetc(pf));
    printf("%c\n", fgetc(pf));
    printf("%c\n", fgetc(pf));
    rewind(pf);
    printf("%c\n", fgetc(pf));

    fclose(pf);
    pf = NULL;
}


文本文件,二进制文件 

1. 根据数据的组织形式,数据文件被分为文本文件与二进制文件。

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

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

.

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

1. 字符一律以ASCII形式存储。

2. 数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

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

二进制编辑器打开


文件读取结束的判定 

1. 文本文件读取结束的判断

用 fgetc 判断返回值是否为 EOF 或用 fgets 判断返回值是否为 NULL。

.

2. 二进制文件读取结束的判断

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

.

3. 正确使用 feof

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

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


文件缓冲区

1. 系统会自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。

2. 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

3. 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输到内存缓冲区,充满缓冲区后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。

4. 缓冲区的大小根据编译器决定的。

.

这样做的原因:

1. 当我们使用读写函数的时候,这些函数底层会调用操作系统的接口,让操作系统帮我们读写数据。

2. 实际上操作系统运行着很多进程,如果我们频繁使用这些函数,就会频繁的打断操作系统,这样操作系统就罢工了。

3. 所以我们设一个缓冲区,放满了才去麻烦操作系统。

.

例子

结论:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】实验三

题目: 1、如何使用C来找出编码88表示的字符?指出至少两种方法。 思路:方法一定义一个字符型变量直接等于88,将其输出结果为编码88表示的字符;方法二使用整形变量来存储88,输出时将其强制转换成字符型。 …

GD32 MCU如何将烧录口配置为GPIO使用?

如果大家在进行GD32 MCU开发时发现GPIO引脚使用不足,可以尝试将烧录口配置为GPIO使用,这样就可以多出几个引脚使用,但使用的时候如何配置以及有哪些注意事项,本视频将会为大家进行解答。 GD32 MCU存在两种GPIO备用功能的配置&…

数字陷波器的设计和仿真(Matlab+C)

目录 一、数字陷波器的模型 二、Matlab仿真 1. 示例1 2. 示例2 三、C语言仿真 1. 由系统函数计算差分方程 2. 示例代码 一、数字陷波器的模型 二、Matlab仿真 1. 示例1 clear clc f0=100;%滤掉的100Hz fs=1000;%大于两倍的信号最高频率 r=0.9; w0=2*pi*f0/fs;%转换到…

【深度学习】语音合成,TTS,fish-speech

官方项目地址:https://github.com/fishaudio/fish-speech git clone https://github.com/fishaudio/fish-speech.gitdocker run -it --gpus device3 -v /ssd/xiedong/tts:/ssd/xiedong/tts --net host --shm-size 16G kevinchina/deeplearning:pytorch2.3.0-cuda12.…

玩转CSS:用ul li +JS 模拟select,避坑浏览器不兼容。

玩转CSS:用ul li JS 模拟select,避坑浏览器不兼容。 在前端的工作中,经常会遇到 selcet控件,但我们用css来写它的样式时候,总是不那么令人满意,各种浏览器不兼容啊有没有? 那么,我…

Datawhale AI 夏令营——AI+逻辑推理——Task1

# Datawhale AI 夏令营 夏令营手册:从零入门 AI 逻辑推理 比赛:第二届世界科学智能大赛逻辑推理赛道:复杂推理能力评估 代码运行平台:魔搭社区 比赛任务 本次比赛提供基于自然语言的逻辑推理问题,涉及多样的场景&…

27K star!有没有显卡都能搞,快速基于LLM构建本地智能知识库

觉得搞一个AI的智能问答知识库很难吗?那是你没有找对方向和工具, 今天我们分享一个开源项目,帮助你快速构建基于Langchain 和LLM 的本地知识库问答,在GitHub已经获得27K star,它就是:Langchain-Chatchat L…

在Spring项目中使用Maven和BCrypt来实现修改密码功能

简介 在数字时代,信息安全的重要性不言而喻,尤其当涉及到个人隐私和账户安全时。每天,无数的用户登录各种在线服务,从社交媒体到银行账户,再到电子邮件和云存储服务。这些服务的背后,是复杂的系统架构&am…

进程间关系

目录 亲缘关系 进程组关系 会话关系 孤儿态进程 亲缘关系 亲缘关系主要体现于父子进程,子进程父进程创建,代码继承于父进程,父进程负责回收,子进程诞生至结束父进程全程参与,这种称为强亲缘关系。 系统开机后&…

企业级数据分析平台合集介绍

企业发展离不开数据分析,数据分析推动着企业运营、决策和战略规划。它正逐步深入到各行各业的核心业务流程中,从传统的金融、零售、制造业扩展到医疗健康、教育、能源等更多领域。企业正通过数据分析平台实现数据资源的最大化利用,推动业务与…

wireshark--流量分析利器

🎼个人主页:金灰 😎作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️ 🍊易编橙终身成长社群&#…

基于微信小程序的课堂考勤系统的设计与实现(论文+源码)_kaic

基于微信小程序的课堂考勤系统的设计与实现 摘 要 在高校教育普及的今天,学生人数日益增多,为保证课堂质量,教师多要在课前进行考勤。因此本设计提出基于微信小程序的课堂考勤系统,增加了定位功能,避免了“假打卡”…

探索大型语言模型LLama 2:原理揭秘与代码实践

一、引言 1.1 大型语言模型的重要性 大型语言模型作为人工智能领域的重要研究方向,近年来取得了显著的成果。这些模型在自然语言处理、机器翻译、对话系统、文本生成等领域展现了强大的能力,为人类带来了诸多便利。大型语言模型的出现,使得…

卓码软件测评:软件功能测试和非功能测试详情介绍

随着信息技术的不断发展,软件在我们日常生活与工作中扮演着越来越重要的角色。然而,软件质量的好坏直接关系到使用者的体验和企业的声誉。在软件开发过程中,功能测试和非功能测试作为保证软件质量的重要手段,受到了越来越多的关注…

web后端--Spring事务管理

事务也要日志配置 !!!!debug前面记得加空格 logging:level:org.springframework.jdbc.support.JdbcTransactionManager: debugrollbackFor 默认情况下,只有出现RunTimeException才会回滚事务,rollbackfor属性用于控制出现何种异常类型,回滚…

Flink内存管理机制

前言 在Flink的后台界面,可以看到整个Flink的内存情况。 如JobManager的内存情况: TaskManager的内存情况 一、Flink内存管理 Flink TaskManager内存组成整体结构图如下: 二、总内存管理 三、JobManager内存管理内存管理 四、TaskManager内…

运算符优先级、赋值运算符、一元运算符、逻辑运算符

运算符优先级 字符串 布尔 null undefined 赋值运算符 一元运算符 逻辑运算符 && 逻辑与 ||逻辑或 ??空值合并运算符 称为空值合并运算符,它是ES6的一个新特性,它的作用是当一个表达式是nul或者undefined时为变量设…

Pip换源使用帮助

PyPI 镜像使用帮助 PyPI 镜像帮助提高包安装的速度,特别是当默认源访问较慢时。镜像每次同步成功后,每隔 5 分钟进行更新,确保镜像内容尽量与官方源保持一致。 pip 临时使用 如果您只想在一次安装中使用镜像,可以使用以下命令&…

嵌入式到底是啥嵌入了啥?

嵌入式系统(Embedded System)是指一个专用的计算机系统,它作为一个装置或系统的一部分被嵌入其中,来实现特定的功能。我收集归类了一份嵌入式学习包,对于新手而言简直不要太棒,里面包括了新手各个时期的学习…

项目比赛经验分享:如何让即兴发言出彩

项目比赛经验分享:如何让即兴发言出彩 前言1. 顺势趁便法2. 词语撮要法3. 起承转合法4. 数字串连法结语 在项目管理和比赛的激烈竞争中,即兴发言往往成为展示个人魅力和团队精神的重要环节。如何在短时间内组织语言,表达清晰、有力的观点&…