关于文件操作---C语言

引言

关于文件,想必大家或多或少都会有些了解,文件可以帮我们储存数据,不同格式的文件可以储存不同类型的数据,也可以将文件中的数据用不同的方式打开。电脑中的文件,是放在硬盘上的。在我们编写代码并运行的时候,如果没有文件,我们写的程序数据只会在电脑内存中,一旦我们退出程序,内存便会回收,数据会丢失,为了将数据持久化,我们可能需要使用文件。

关于文件

在我们的程序设计中,分为两种文件,一个是程序文件(用来存放运行代码,如:test.c,test.obj),另一个是数据文件(用来存放程序运行时读写的数据)。

其中,数据文件还可以分为:

1.文本文件:以ASCII字符形式存储

2.二进制文件:以二进制形式存储

每一个文件都有一个唯一表示而标识分为三部分:

文件路径 + 文件名主干 + 问价后缀

eg:C:\code\test.exe

关于文件操作

这里先给大家举个例子吧

#include<stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("text.txt", "wb");//以二进制只写方式打开文件text.txtfwrite(&a, 4, 1, pf);//以二进制形式写入afclose(pf);//关闭pf指向文件pf = NULL;//指针制空,避免野指针return 0;
}

根据以上这一份代码,目前不需要完全看懂,这时你只需要了解到操作文件的一个基本的流程

操作文件流程:

1.打开文件

2.读/写文件

3.关闭文件 

关于文件的操作,还引入了流这个概念。那么流到底是什么呢?其实你可以从字面上去理解它,就是一条小河,不过里面流动的不是水,而是数据,流作为一个连接程序运行和外部设备的一个中间媒介,起着传输数据的作用。有了流,程序在运行中才能读取键盘输入或文件中保存的数据;有了流,才能将程序运行时产生的数据打印到屏幕上或写到文件中。

将这一概念抽象成图是这样的:

流这个中间媒介是抽象出来的,具体的底层实现我们可以先不了解,但是我们需要了解如何正确打开和使用流。

我相信能看到文件操作读者以及对C语言有了相当的了解,可能你会问,为什么我们在之前写代码的时候直接就用scanf和printf函数了,没有见到打开和关闭流的操作啊?

那是因为,这个工作C语言系统已经事先帮我们做好了

在C语言程序中,默认打开三个流:

1.stdin:标准输入流

2.stdout:标准输出流

3.stderr:标准错误流

这几个流的类型都是FILE*(文件指针)

这又说到了文件指针

文件指针(文件类型指针)

文件在内存中有一个文件信息区,用来存放文件信息,这些信息被放在一个结构体变量中,这个结构体就是FILE

在C语言的Visual Studio 2013中,FILE是一个系统定义的结构体类型,用于表示一个文件的相关信息。在stdio.h头文件中,FILE结构体被定义为:

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


在这个结构体中,包含了文件名、文件状态和文件当前位置等信息。通过使用指向FILE结构体的指针,可以更加方便地对文件进行操作。

这个结构体格式也不需要大家背下来,只是便于大家理解FILE,不同C编译器中FILE内容不完全相同,但总的来说大同小异,只要最终实现的功能相同就没什么大问题。

我们在使用fopen打开文件的时候,将这一函数返回的FILE*类型的指针返回给pf。这样,在下次读写的时候,就可以根据pf指针来寻找需要读取和写入的文件了。

最后用完pf这一指针,也可以通过pf找到需要关闭的文件进行close操作。

文件的打开和关闭

fopen

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

其中filename为文件名,mode为打开方式

关于文件打开方式mode:

  1. “r” 以"只读"的方式打开一个文本文件(只能读),同时所读的文件必须存在。
  2. “r+” 与"r"的区别在于可以"写"(只能写)。
  3. “rb” 打开一个二进制文件(只能读),同时所读的文件必须存在。
  4. “rb+” 与"rb"的区别在于可以"写",同时也可以读。
  5. “w” 以"写"的方式创建一个文本文件,如果这个文件不存在,将创建一个此文件名的文件写入;如果存在,则将原文件内容清空并覆盖。
  6. “w+” 与"w"的区别在于,增加了"读"。
  7. “wb” 以"写"的方式创建一个二进制文件。
  8. “wb+” 与"wb"的区别在于,增加了"读"。
  9. “a” 以"尾部追加"的方式打开一个文本文件(只能写)。
  10. “a+” 与"a"的区别在于,增加了"读"。
  11. “ab” 以"尾部追加"的方式打开一个二进制文件(只能写)。
  12. “ab+” 与"ab"的区别在于,增加了"读"。

下面写一份代码观察一下

#include<stdio.h>
int main()
{//打开文件FILE* pf = fopen("data.txt", "w");if (pf == NULL) {//检查文件是否正常打开perror("fopen");//如没有正常打开,打印错误原因return 1;}//写文件//。。。//关闭文件fclose(pf);pf = NULL;return 0;
}

上述代码便是用只写的方式打开的

下面讲一点拓展知识,在你实际操作的过程中,也许你注意到data.txt的路径是在当前文件下的,但是,如果你想操作的文件不再当前路径或者想生成的文件不再当前路径怎么办呢?这时候,就可以使用我们的相对路径。

在文件操作中 表示当前目录,而 ..  表示上一级路径

比如说:./../../data.txt 表示的便是返回上两级路径。

文件的顺序读写和随机读写

文件的顺序读写

和gets,scanf和printf等函数一样,文件的读写也有其对应的函数,下面给大家列出来,然后两两讲解

1.fgetc和fputc

2.fgets和fputs

3.fscanf和fprintf

4.fread和fwrite

前三组可针对所有输入输出流

最后一组只能针对二进制文件输入输出

fgetc和fputc

int fgetc(FILE* stream);

int fputc(int charater,FILE* stream);

为了方便,我们先讲fputs,下面见代码

#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL) {perror("fopen");return 1;}//写文件for (int i = 0; i < 26; i++) {fputc('a' + i, pf);fputc('\n', pf);}fclose(pf);pf == NULL;return 0;
}

可以在代码里运用fput来往文件中写字符 ,以下是写入文件中的结果

然后就是fgetc,大家应该就能猜到其作用了,这个函数可以从文件中一个个读取字符,每读取一个字符光标往后移动一位,见具体使用代码。

#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}int ch = fgetc(pf);printf("%c\n", ch);fclose(pf);pf = NULL;return 0;
}

读取上次在fputc的时候写入的数据,就可以通过data.txt文件打印出a

下面展示一份可以将一个文件中的内容转移到另一个文件中的代码,也是关于,fgetc和fputc的运用

#include<stdio.h>
int main()
{FILE* pfread = fopen("data1.txt", "r");if (pfread == NULL) {perror("fopen->data1.txt");return 1;}FILE* pfwrite = fopen("data2.txt", "w");if (pfwrite == NULL) {perror("fopen->data2.txt");return 1;}char ch;while ((ch = fgetc(pfread)) != EOF)fputc(ch, pfwrite);fclose(pfwrite);fclose(pfread);pfread = pfwrite = NULL;return 0;
}

 

其中data1.txt中的文件是我随意粘上去的,最后全部写入了data2.txt中,这个代码功能还是很强大的。

fgets和fputs

char* fgets(char* str,int num,FILE* stream);读字符串(最多读num个包含'\0')

int fputs(const char* str,FILE* stream);写字符串(一行)

其中,str表示的就是字符串的读入和写出

#include<stdio.h>
int main()
{FILE* pfread = fopen("data1.txt", "r");if (pfread == NULL) {perror("fopen->data1.txt");return 1;}FILE* pfwrite = fopen("data2.txt", "w");if (pfwrite == NULL) {perror("fopen->data2.txt");return 1;}char arr[20];fgets(arr, 19, pfread);fputs(arr, pfwrite);fclose(pfwrite);fclose(pfread);pfread = pfwrite = NULL;return 0;
}

上述代码是将从data1.txt中读到的一行字符写入data2.txt中。

 fscanf和fprintf

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

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

在我看来,这两个函数是我介绍的io函数中功能相对强大的,这两个函数的功能与printf和scanf极其相似,只是多了前面的stream文件指针,可以见一下下面的代码见见其功能

#include<stdio.h>
struct Stu
{char name[20];int age;float score;
};
int main()
{struct Stu s = {0};FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}FILE* pfw = fopen("data1.txt", "w");if (pfw == NULL) {return 1;}fscanf(pf, "%s %d %f", s.name, &s.age, &s.score);fprintf(pfw, "%s %d %.1f", s.name, s.age, s.score);fclose(pf);pf == NULL;return 0;
}

 

在运行结束后data.txt中的内容就成功写道data1.txt中去了 。

fread和fwrite

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

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

第一个参数代表位置,第二个代表大小,第三个代表个数,第四个是文件指针

下面来看代码使用

#include<stdio.h>
struct Stu
{char name[20];int age;float score;
};
int main()
{struct Stu s = {0};FILE* pf = fopen("data.txt", "rb+");if (pf == NULL)return 1;fread(&s, sizeof(s), 1, pf);printf("%s %d %.1f", s.name, s.age, s.score);fwrite(&s, sizeof(s), 1, pf);fclose(pf);pf = NULL;return 0;
}

最后就可以以二进制的形式将文件写入另一个文件了,只不过由于是文本文件打开的原因,其解码形式是文本文件的形式,所以数字打印出来的码是看不懂的。

文件的随机读写

文件的随机读写,意思不是随机的读写文件中的数据,而是可控的控制光标读写入和读取文件中的数据。相关的函数有,fseek,ftell和rewind。

fseek

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

其中stream表示的是文件指针,offset表示的是光标偏移量,origin可以控制光标的起始位置

ftell

long int ftell(FILE* stream);

stream表示文件指针

rewind

void rewind(FILE* stream);

stream表示文件指针

使用见代码

#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL) {return 1;}fseek(pf, -4, SEEK_CUR);//从当前光标向前移动4位fseek(pf, -6, SEEK_END);//从末尾光标向前移动6位fseek(pf, 0, SEEK_SET);//将光标移到初始位int n = ftell(pf);//返回光标相对起始位置的偏移量rewind(pf);//将文件指针返回起始位置fclose(pf);pf = NULL;return 0;
}

 关于对文件读取结束的判定

错误使用feof:不能用其返回值来判断文件是否结束,feof是用来判断文件是否是因为文件正常读到文件尾而结束。

判断是否结束可以判断(EOF)fgetc,或(NULL)fgets

fread函数返回值是其读取元素个数

文件缓冲区

ANSIC采用“缓冲区系统”处理数据文件

系统自动为程序每一个正在使用的文件开辟一块“文件缓冲区”

刷新缓冲区函数

fflush(FILE* stream);

注:fclose也有刷新缓冲区的效果 

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

结语

今天分享了关于文件操作相关的知识,创作不易,如果觉得本篇博客内容还不错的话,还请点小小的赞再走,也可以收藏以下本篇博客,如果感兴趣的话还可以给我点个关注,后续还会分享更多有意思的内容---比心♥

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

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

相关文章

vue中的常见使用

文章目录 datacomputedwatch深层监听 methods 这里是记录一下常规的使用 data 最常用的&#xff0c;定义一些使用到的数据&#xff0c;以一种函数的表现方式 data(){return{} }声明的数据写在return中 computed 较少使用&#xff0c;称为计算属性。 从名称和使用方式来看&a…

谣言检测常用评价指标

谣言检测通常是一个二分类任务&#xff0c;常用评价指标包括Precision、Recall、Accuracy、F1-score、Micro-F1、Macro-F1等。 Precision和Recall 名称含义TP(True Positive)真阳性 预测为正&#xff0c;实际为正FP(False Positive)假阳性 预测为正&#xff0c;实际为负TN(Tr…

金蝶云星空表单插件获取复选框的值

文章目录 金蝶云星空表单插件获取复选框的值 金蝶云星空表单插件获取复选框的值 object getPur this.View.Model.GetValue("F_XHWT_IsPur", rowIndexV);bool isSerial !Convert.ToBoolean(itemClose["F_XHWT_IsPur"] "");取得值可以直接转换成…

Datawhale聪明办法学Python(task3变量与函数)

一、课程基本结构 课程开源地址&#xff1a;课程简介 - 聪明办法学 Python 第二版 章节结构&#xff1a; Chapter 0 安装 Installation Chapter 1 启航 Getting Started Chapter 2 数据类型和操作 Data Types and Operators Chapter 3 变量与函数 Variables and Functions Ch…

论文写作☞结论怎么写?

论文的结论部分是整篇论文的总结和归纳&#xff0c;需要简明扼要地概括研究的主要内容和发现&#xff0c;同时也需要回答研究问题和假设。以下是结论部分的写作建议&#xff1a; 1.首先&#xff0c;简要概括研究的主要内容和发现&#xff0c;强调研究的重要性和贡献。 2.接着…

Cortex-M4内核结构

Cortex-M4内核结构 1. 内核Core 2. 三阶流水线 3. 内核工作模式 4. 总结 Cortex-M4内核结构 Cortex-M4处理器是ARMv7-M架构的一种实现&#xff0c;它是一种32位精简指令集(Reduced Instruction Set Computing, RISC)的处理器&#xff0c;有一个三阶的指令流水线&#xff0c;依…

飞天使-docker知识点4-harbor

文章目录 Harbor安装完成harbor 官方建议方式之后查看 images配置docker 使用harbor 仓库上传下载镜像docker 镜像结合harbor 运行 Harbor Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器&#xff0c;由 vmware 开源&#xff0c;其通过添加一些企业必需的功…

C++初阶-list类的模拟实现

list类的模拟实现 一、基本框架1.1 节点类1.2 迭代器类1.3 list类 二、构造函数和析构函数2.1 构造函数2.2 析构函数 三、operator的重载和拷贝构造3.1 operator的重载3.2 拷贝构造 四、迭代器的实现4.1 迭代器类中的各种操作4.1 list类中的迭代器 五、list的增容和删除5.1 尾插…

leetcode 股票问题全序列

1 只允许一次交易&#xff0c;121题&#xff0c;买卖股票的最佳时机 class Solution {/*给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票…

Python机器学习预测房价

&#x1f517; 运行环境&#xff1a;Python &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 &#x1f510;#### 防伪水印——左手の明天 ####&#x1f510; &#x1f497; 大家…

Visual Studio 2022封装C代码为x64和x86平台动态库

1.引言 本文介绍如何使用Visual Studio 2022将C语言函数封装成x64和x86平台上使用的动态链接库(dll文件)并生成对应的静态链接库(lib文件)&#xff0c;以及如何在C程序中调用生成的dll。 程序下载&#xff1a; 2.示例C语言程序 假设需要开发一个动态链接库&#xff0c;实现复…

为什么大多数TCP服务器采用多线程,以及如何从头开始构建一个多线程的TCP服务器

目录 前言 了解TCP服务器 设置TCP服务器 接受客户端的连接 读取并响应请求 实现一个循环以进行连续操作 Java举例 结论 前言 TCP是一种在计算机网络中用于机器之间通信的最可靠的方法在本文中&#xff0c;我们将探讨一个Web服务器如何处理和为多个TCP连接提供服务为了更…

Python exec() 函数初探

你好&#x1f596;&#xff0c;我是简讯&#xff01;一枚野生程序员。热爱编程&#xff0c;但目前的工作与编程毫无关系。 正在尝试各种副业&#xff0c;目前有做&#xff1a; 红包封面商城中视频计划 想要零成本一起参与红包封面商城的可以看这篇文章&#xff1a;副业实践&…

for-each循环优先于传统的for循环

在大多数情况下&#xff0c;使用 for-each 循环&#xff08;也称为增强型 for 循环&#xff09;比传统的 for 循环更加简洁和方便。它提供了一种更直观的方式来遍历集合或数组中的元素。以下是一个例子&#xff0c;展示了 for-each 循环优于传统 for 循环的情况&#xff1a; i…

AI智能配音助手微信小程序前后端源码支持多种声音场景选择

大家好今天给大家带来一款配音小程序 &#xff0c;这款小程序支持多种不同声音和场景的选择更人性化&#xff0c; 比如说支持各地区的方言,英文,童声呀等等、 另外也支持男声女声的选择,反正就是模板那些非常的多 当然啦音量,语调,语速那些都是可以DIY跳转的哟,所以说这一款小程…

生信算法4 - 获取overlap序列索引和序列的算法

生信序列基本操作算法 建议在Jupyter实践&#xff0c;python版本3.9 1. 获取overlap序列索引和序列的算法实现 # min_length 最小overlap碱基数量3个 def getOverlapIndexAndSequence(a, b, min_length3):""" Return length of longest suffix of a matching…

python每日学11:xpath的使用与调试

背景&#xff1a;最近在使用selenium 模拟浏览器作一些常规操作&#xff0c;在使用selenium的过程中接触到的一种定位方法&#xff0c;叫xpath, 这里说一下使用心得。 首先&#xff0c;我觉得如果只是简单使用的话是不用详细了解具体的语法规则的。 一、xpath怎么用&#xff1…

树莓派(Raspberry Pi)4B密码忘记了,怎么办?

树莓派长时间不用&#xff0c;导致密码忘记了&#xff0c;这可咋整&#xff1f; 第1步&#xff1a;取出SD卡 将树莓派关机&#xff0c;移除sd卡&#xff0c;使用读卡器&#xff0c;插入到你的电脑。 第2步&#xff1a;编辑 cmdline.txt 在PC上打开SD卡根目录&#xff0c;启动…

PyQt5系列

基本使用 1、创建应用&#xff0c;初始化UI界面&#xff0c;创建布局盒子 # -*- coding: utf-8 -*- # Creator: zhu # Time: 2023-12-13 import xlwt import sys, base64, xlrd, re, datetime from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QApplication, QW…

windows下类Unix模拟环境,msys2,mingw,gcc的安装与环境配置

windows下常用的类Unix模拟环境&#xff1a; git fow windows&#xff1a;git等mingw&#xff1a;打包了gcc等编译器cygwin&#xff1a;大而全的类Unix环境支持msys2&#xff1a;自带大量预编译的软件包&#xff0c;提供pacman软件管理工具&#xff0c;能安装Unix下大部分软件…