C语言——文件操作

目录

前言

一什么是文件

1程序文件

2数据文件

3文件名

二文件的打开与关闭

1文件指针

2fopen

3fclose 

三文件的读与写

1文件的顺序读写

1.1fputc fgetc

1.2fputs fgets

1.3fprintf fscanf

1.4fwrite fread

1.5文本文件和二进制文件

2文件的任意读写

1fseek

2ftell

3rewind

四文件读取结束

五文件缓冲区

六改写通讯录


前言

在前面写通讯录时,我们发现:每次程序结束后通讯录内的消息就不见了,原因是:信息都是在内存中储存;如果我们要想把信息存储起来,就需要和文件打交道,那么C语言中又是怎么对文件进行操作呢?看完这篇文章或许你就懂了!

一什么是文件

磁盘上保存的数据都叫做文件;
在程序设计中,我们一般谈的文件有两种:程序文件、数据文件;

1程序文件

包括源程序文件(后缀为.c),目标文件(后缀为.obj),可执行程序(后缀为.exe)

2数据文件

文件的内容不一定是程序,而是程序运行时读写的数据;                                                              比如程序运行需要从中读取数据的文件,或者输出内容的文件   

3文件名

用来标识文件唯一性;文件名通常是一个完整的文件路径(c:\code\test.txt)

二文件的打开与关闭

1文件指针

缓冲文件系统中,有个概念叫“文件类型指针”,简称“文件指针”;                                                      打开一个文件时都会在内存中开辟一个结构体,用来储存文件的相关信息(文件名,大小...),C语言把这种结构体定义为FILE:

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

而FILE通常是以指针的形式:                                                                                                    一是程序员方便使用

二是在内存中可能有很多FILE结构体,要想更好地管理它们:用类似链表的方式实现

2fopen

第一个参数是文件名(可以是相对路径(.\ ..\进行表示)或者绝对路径(完整路径));

第二个参数是你要用什么方式打开文件

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

3fclose 

与申请内存类似:文件操作完后也要对文件进行关闭

使用:

#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen error");return 1;}//使用...fclose(pf);pf = NULL;return 0;
}

注意:对于文件后缀名:建议把它进行开启,否则可以会出现以下错误:

明明在当前路径下有该文件,就是找不到!原来该文件的后缀名是被隐藏掉了,真正的文件名是:

三文件的读与写

1文件的顺序读写

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

这里所说的:输入输出流是怎么一回事?

流是一种抽象的概念,我们可将其看成‘水流’(数据流):当我们要接水时:我们可以用盆接,用碗接,用手接...同样,我们要接收数据(输入流)也有多种方式,fgetc,fgets...用水的情况也有多种,我们使用数据(输出流)处理具体情况的方式也有所不同,决定了程序员在不同情况下要掌握不同函数调用来使用数据的能力;常见的输入输出流的例子:                 我们平时说:用scanf接收数据,把数据打印到屏幕上:本质上就是输入输出流;那么你可能会说:我在使用scanf或者printf时是不用传入FILE*的指针变量就能完成,这怎么可能是输入输出流?

其实:在程序运行时,程序默认为我们打开了三个流:stdout(标准输出流),stdin(标准输入流),stderror(标准错误流),这三个变量都是FILE*类型!

使用scanf或者printf时底层默认使用指定的标准流来处理数据的~

1.1fputc fgetc

向指定流里面写入/读取一个字符

使用:data.txt文件写入26个字母

int main()
{FILE* pf1 = fopen("data.txt", "w");if (pf1 == NULL){perror("fopen error");return 1;}char ch = 0;for (ch = 'a'; ch <= 'z'; ch++){fputc(ch, pf1);}fclose(pf);pf = NULL;return 0;
}

data.txt文件读取26个字母

int main()
{FILE* pf = fopen("data.txt", "r");//注意这个文件操作的方式是"r"if (pf == NULL){perror("fopen error");return 1;}char ch = 0;//fgetc读到文件结尾后返回EOF(-1)while (1){ch = fgetc(pf);if (ch == EOF) break;printf("%c ", ch);}fclose(pf);pf = NULL;return 0;
}

使用stdout则将数据输出到屏幕上,达到与printf一样的效果

1.2fputs fgets

向指定流里写入/读取一行字符串

向data.txt中写入两行字符串

int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen error");return 1;}fputs("hello\n", pf);fputs("world\n", pf);fclose(pf);pf = NULL;return 0;
}

从data.txt读取第一行的三个字符

#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen error");return 1;}char arr[10] = { 0 };fgets(arr, 3+1, pf);//读取的字符总数包含了\n,所以决定读n个字符参数传n+1printf("%s", arr);fclose(pf);pf = NULL;return 0;
}

1.3fprintf fscanf

 

格式化数据(如结构体)写入/读取数据

将格式化数据写入data.txt 

#include<stdio.h>struct S
{float f;char c;int i;
};int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen error");return 1;}struct S s = { 3.14,'w',100 };fprintf(pf, "%f-%c-%d", s.f, s.c, s.i);fclose(pf);pf = NULL;return 0;
}

把data.txt里的格式化数据读出来(读出的格式需要与写入的格式保持一致

#include<stdio.h>struct S
{float f;char c;int i;
};int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen error");return 1;}struct S s = {0};//fprintf(pf, "%f-%c-%d", s.f, s.c, s.i);fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.i));//格式保存一致printf("%f %c %d", s.f, s.c, s.i);fclose(pf);pf = NULL;return 0;
}

1.4fwrite fread

以二进制格式写入/读取

使用fwrite向文件写进5个数:

#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen error");return 1;}int arr[] = { 1,2,3,4,5 };fwrite(arr, sizeof(int), 5, pf);fclose(pf);pf = NULL;return 0;
}

 

发现都是乱码(二进制),那这些到底是不是我们写进去的数据呢?

用fread读取后打印出来看看:

#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "rb");if (pf == NULL){perror("fopen error");return 1;}int arr[5] = {0};fread(arr, sizeof(int), 5, pf);for (int i = 0; i < 5; i++){printf("%d ", arr[i]);}fclose(pf);pf = NULL;return 0;
}

前面的三组函数都是以文本文件进行写入/读取,而fwrite/fread以二进制文件进行写入/读取,这两者要怎么理解?

1.5文本文件和二进制文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

如果用一个整数以二进制格式写到文件中,要想看到对内容进行‘翻译’:可以将二进制文件拖到VS中以二进制编译器的形式打开:内容是整数在内存中的储存形式(vs:小端储存的十六进制)

#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;
}

2文件的任意读写

1fseek

根据文件指针的位置和偏移量来定位文件指针(想读哪个文件指针就定位到哪里)

关于origin的参数: 

使用:通过三个参数找到文件的最后一个字符

#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen error");return 1;}//data.txt的数据:abcdechar ch=fgetc(pf);printf("%c\n", ch);//读出最后一个字符e//fseek(pf, -1, SEEK_END);//SEEK_END定位到的是\n,往前偏移一位才能找到e//fseek(pf, 4, SEEK_SET);//开始位置完后偏移4找到e(要计算)fseek(pf, 3, SEEK_CUR);//当前位置往后偏移3找到e(要计算)ch = fgetc(pf);printf("%c", ch);fclose(pf);pf = NULL;return 0;
}

2ftell

 

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

3rewind

让文件指针从起始位置开始

四文件读取结束

网上或者教材上很多说:用 feof 函数进行判断文件是否结束,其实是不严谨的;                        feof作用:当文件读取结束后,判断是否遇到文件结束标志而结束                                                  与ferrof作用对比: 当文件读取结束后,判断是否遇到错误而结束

1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数                                              (如果正常使用write/read 写入/读取多少个,正常来说是会返回写入/读到的个数)

使用:将test1.txt的内容拷贝到test2.txt中

#include <stdio.h>
int main(void)
{int c; // 注意:int,非charFILE* fp1 = fopen("test1.txt", "r");if (!fp1){perror("File opening failed");return 1;}FILE* fp2 = fopen("test2.txt", "w");if (!fp2){perror("File opening failed");fclose(fp1);fp1 = NULL;return 2;}//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOFwhile ((c = fgetc(fp1)) != EOF) // 标准C I/O读取文件循环{fputc(c, fp2);}fclose(fp1);fp1 = NULL;fclose(fp2);fp2 = NULL;return 0;
}

五文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”

写入文件的数据先保存在“文件缓冲区”中,等到调用相关函数把文件缓冲区的数据刷新到文件中(close()),才是真正地把数据保存在文件中

scanf()读键盘数据也是同理:所以我们在用scanf读入字符串时要注意缓冲区的'\n'问题

//测试文件缓冲区存在的代码#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
int main()
{FILE* pf = fopen("test.txt", "w");fputs("abcdef", pf);//先将代码放在输出缓冲区printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");Sleep(10000);fclose(pf);//fclose在关闭文件的时候,也会刷新缓冲区pf = NULL;return 0;
}

六改写通讯录

学习完文件操作后,是时候解决前言提出的问题:把通讯录改造成不受程序关闭而通讯录信息丢失

1.在程序退出时,把当前通讯录的信息保存在Contact.txt文件中(使用fwrite)                                    2.在程序刚开始初始化通讯录时,把Contact.txt文件的数据加载到内存中(使用fread)

void CheckCapacity(Contact* con)
{if (con->sz == con->capacity){//扩容Mess* ptr = realloc(con->message, (con->capacity + Default_Size) * sizeof(Mess));if (ptr != NULL){con->message = ptr;con->capacity += Default_Size;printf("扩容成功\n");}else{perror("realloc error");return;}}
}void InitContact(Contact* con)
{//文件版本:对上一次的通讯录数据进行拷贝con->message = (Mess*)malloc(Default_Size * sizeof(Mess));if (con->message == NULL){perror("malloc error");return 1;}con->sz = 0;con->capacity = Default_Capacity; FILE* pf = fopen("Contact.txt", "rb");if (pf == NULL){perror("fopen fail");return 1;}Mess tmp = { 0 };while (fread(&tmp, sizeof(Mess), 1, pf)){CheckCapacity(con);con->message[con->sz] = tmp;con->sz++;}
}void SaveContact(Contact* con)
{FILE* pf = fopen("Contact.txt", "wb");for (int i = 0; i < con->sz; i++){fwrite(con->message + i, sizeof(Mess), 1, pf);}
}

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

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

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

相关文章

亚博microros小车-原生ubuntu支持系列:1 键盘控制

背景&#xff1a;电脑配置不太行&#xff0c;我在ubuntu再运行vmware&#xff0c;里面运行亚博官方的虚拟机镜像ubuntu&#xff0c;系统很卡。基本上8G内存给打满了。还是想把亚博官方的代码迁移出来&#xff0c;之前售后就说除了官方镜像虚拟机&#xff0c;需要自己摸索迁移。…

基于Redis实现短信验证码登录

目录 1 基于Session实现短信验证码登录 2 配置登录拦截器 3 配置完拦截器还需将自定义拦截器添加到SpringMVC的拦截器列表中 才能生效 4 Session集群共享问题 5 基于Redis实现短信验证码登录 6 Hash 结构与 String 结构类型的比较 7 Redis替代Session需要考虑的问题 8 …

Golang Gin系列-4:Gin Framework入门教程

在本章中&#xff0c;我们将深入研究Gin&#xff0c;一个强大的Go语言web框架。我们将揭示制作一个简单的Gin应用程序的过程&#xff0c;揭示处理路由和请求的复杂性。此外&#xff0c;我们将探索基本中间件的实现&#xff0c;揭示精确定义路由和路由参数的技术。此外&#xff…

Asp.Net Core 8.0 使用 Serilog 按日志级别写入日志文件的两种方式

1、所需的Nuget包 本文项目的版本是.NET 8.0&#xff0c;如果使用其它版本安装适配版本即可。 Serilog.AspNetCore(8.0.2) Serilog.Sinks.File(5.0.0) Serilog.Expressions(5.0.0) 2、两种配置方式 2.1 代码形式&#xff08;Program.cs&#xff09; 在Program.cs文件中&am…

STM32+W5500+以太网应用开发+003_TCP服务器添加OLED(u8g2)显示状态

STM32W5500以太网应用开发003_TCP服务器添加OLED&#xff08;u8g2&#xff09;显示状态 实验效果3-TCP服务器OLED1 拷贝显示驱动代码1.1 拷贝源代码1.2 将源代码添加到工程1.3 修改代码优化等级1.4 添加头文件路径1.5 修改STM32CubeMX工程 2 修改源代码2.1 添加头文件2.2 main函…

【深度学习项目】语义分割-DeepLab网络(DeepLabV3介绍、基于Pytorch实现DeepLabV3网络)

文章目录 介绍深度学习语义分割的关键特点主要架构和技术数据集和评价指标总结 DeepLabDeepLab 的核心技术DeepLab 的发展历史DeepLab V3网络结构获取多尺度信息架构Cascade ModelASPP ModelMulti-GridPytorch官方实现的DeepLab V3该项目主要是来自pytorch官方torchvision模块中…

Golang Gin系列-5:数据模型和数据库

在这篇Gin教程的博客中&#xff0c;我们将探索如何将模型和数据库与Gin框架无缝集成&#xff0c;使你能够构建健壮且可扩展的web应用程序。通过利用流行的库并遵循最佳实践&#xff0c;你将学习如何定义模型、建立数据库连接、执行CRUD操作以及确保基于gin的项目中的数据完整性…

高并发内存池_各层级的框架设计及ThreadCache(线程缓存)申请内存设计

一、高并发内存池框架设计 高并发池框架设计&#xff0c;特别是针对内存池的设计&#xff0c;需要充分考虑多线程环境下&#xff1a; 性能问题锁竞争问题内存碎片问题 高并发内存池的整体框架设计旨在提高内存的申请和释放效率&#xff0c;减少锁竞争和内存碎片。 高并发内存…

如何在idea中搭建SpringBoot项目

如何在idea中快速搭建SpringBoot项目 目录 如何在idea中快速搭建SpringBoot项目前言一、环境准备&#xff1a;搭建前的精心布局 1.下载jdk &#xff08;1&#xff09;安装JDK&#xff1a;&#xff08;2&#xff09;运行安装程序&#xff1a;&#xff08;3&#xff09;设置安装…

elementUI Table组件实现表头吸顶效果

需求描述 当 table 内容过多的时候&#xff0c;页面上滑滚动&#xff0c;表头的信息也会随着被遮挡&#xff0c;无法将表头信息和表格内容对应起来&#xff0c;需要进行表头吸顶 开始编码&#x1f4aa; 环境&#xff1a;vue2.6、element UI step1&#xff1a; 给el-table__h…

[Qt]系统相关-多线程、线程安全问题以及线程的同步机制

目录 一、Qt多线程编程 1.介绍 2.多线程的操作 线程的创建 QThread的常用API 使用案例 3.Qt线程的使用场景 二、线程安全问题 1.互斥锁 介绍 使用案例 2.读写锁 三、线程的同步 1.条件变量 2.信号量 一、Qt多线程编程 1.介绍 Qt中的多线程的底层原理和注意事项…

Linux Bash 中使用重定向运算符的 5 种方法

注&#xff1a;机翻&#xff0c;未校。 Five ways to use redirect operators in Bash Posted: January 22, 2021 | by Damon Garn Redirect operators are a basic but essential part of working at the Bash command line. See how to safely redirect input and output t…

【Linux】环境变量

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;Linux &#x1f339;往期回顾&#x1f339;&#xff1a;【Linux】进程优先级与进程切换 &#x1f516;流水不争&#xff0c;争的是滔滔不 一、环境变量的定义二、命令…

Spring MVC:设置响应

目录 引言 1. 返回静态页面 1.1 Spring 默认扫描路径 1.2 RestController 1.2.1 Controller > 返回页面 1.2.2 ResponseBody 2. 返回 HTML 2.1 RequestMapping 2.1.1 produces(修改响应的 Content-Type) 2.1.2 其他属性 3. 返回 JSON 4. 设置状态码 4.1 HttpSer…

开篇:吴恩达《机器学习》课程及免费旁听方法

课程地址&#xff1a; Machine Learning | Coursera 共包含三个子课程 Supervised Machine Learning: Regression and Classification | Coursera Advanced Learning Algorithms | Coursera Unsupervised Learning, Recommenders, Reinforcement Learning | Coursera 免费…

【C++】模板(进阶)

本篇我们来介绍更多关于C模板的知识。模板初阶移步至&#xff1a;【C】模板&#xff08;初阶&#xff09; 1.非类型模板参数 1.1 非类型模板参数介绍 模板参数可以是类型形参&#xff0c;也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。 //类型模板参数 …

快手SDK接入错误处理经验总结(WebGL方案)

1、打包时提示Assets\WebGLTemplates\ks路径下未找到Index.html文件错误 处理方法&#xff1a;直接使用Unity默认模板下的Index.html文件即可 文件所在路径&#xff1a;Unity安装路径\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates\Default 参考图&a…

用edge浏览器追剧音量太小?安装音量增强器可解忧

0 源起 春节佳节将至&#xff0c;可以利用这个难得的假期追一追想看而没空看的剧了。 但是在用Edge浏览器播放网页中的视频时&#xff0c;有时音量太小&#xff0c;根本没法听清楚&#xff0c; 遇到这种情况时&#xff0c;尽管Edge浏览器本身没有提供音量控制功能&#xff0…

Alluxio 联手 Solidigm 推出针对 AI 工作负载的高级缓存解决方案

作者&#xff1a;Wayne Gao, Yi Wang, Jie Chen, Sarika Mehta Alluxio 作为全球领先的 AI 缓存解决方案供应商&#xff0c; 提供针对 GPU 驱动 AI 负载的高速缓存。其可扩展架构支持数万个节点&#xff0c;能显著降低存储带宽的消耗。Alluxio 在解决 AI 存储挑战方面的前沿技…

Excel 技巧15 - 在Excel中抠图头像,换背景色(★★)

本文讲了如何在Excel中抠图头像&#xff0c;换背景色。 1&#xff0c;如何在Excel中抠图头像&#xff0c;换背景色 大家都知道在PS中可以很容易抠图头像&#xff0c;换背景色&#xff0c;其实Excel中也可以抠简单的图&#xff0c;换背景色。 ※所用头像图片为百度搜索&#x…