当前位置: 首页 > news >正文

C语言详解(文件操作)1

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 前言
  • 一、文件的定义
    • 1.1 什么是文件?
    • 1.2 二进制文件和文本文件
  • 二、文件的打开和关闭
    • 2.1 流和标准流
      • 2.11 流
      • 2.12 标准流
    • 2.2 文件指针
    • 2.3 文件的打开和关闭
      • 2.31 fopen 和 fclose
      • 2.32 文件在当前的工程目录底下
      • 2.33 文件在当前工程目录底下的上一级路径
      • 2.34 文件不在当前工程目录底下
  • 三、文件的随机读写
    • 3.1 fseek
    • 3.2 ftell
    • 3.3 rewind
  • 总结

前言

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


一、文件的定义

1.1 什么是文件?

按文件的功能分类,文件分为程序文件数据文件,文件是存在磁盘(硬盘)上的。

程序文件:源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件

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

标识文件——文件名
一个文件要有一个唯一的文件标识,也就是文件名
文件名包含三部分:文件路径+文件名主干+文件后缀,如:c:\code\test.txt


1.2 二进制文件和文本文件

按文件的内容分类,数据文件又分为二进制文件和文本文件

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

一个数据在文件中是怎么存储的呢?
字符只能以ASCII码的形式存储,数值型数据既可以用ASCII码的形式存储,也可以使用二进制形式存储

比如数字10000,在内存中整型10000是以补码的形式存的,如果以ASCII码形式输出到磁盘,则磁盘中占用5个字节(每个字符占用一个字节),如果以二进制形式输出,则在磁盘中占用4个字节

在这里插入图片描述


二、文件的打开和关闭

2.1 流和标准流

2.11 流

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

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流实现的,一般情况下,我们要想向流里写数据或读数据,都是要打开流,然后操作。


2.12 标准流

以前我们在键盘上输入数据,在屏幕上输出数据,怎么没有打开流呢?
因为C语言程序在启动的时候,默认打开了下面这3个流:

  • stdin:标准输入流,在大多数环境中从键盘输入,scanf函数就是从标准输入流中读取数据
  • stdout:标准输出流,大多数环境中输出到显示器界面,printf函数就是将信息输出到标准输出流中
  • stderr:标准错误流,大多数环境中输出到显示器界面

stdinstdoutstderr三个流的类型是FILE *,通常称为文件指针,C语言中就是通过FILE *的文件指针来维护流的各种操作


2.2 文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件名,文件状态、文件位置等),这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名FILE

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE类型的变量,并填充其中的信息,使用者不必关心细节
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便
例如创建一个FILE *的指针变量:

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

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


2.3 文件的打开和关闭

2.31 fopen 和 fclose

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件
在编写程序的时候,在打开文件的同时,都会返回一个FILE *类型的指针变量指向该文件,也相当于建立了指针和文件的关系
ANSI C规定使用fopen函数来打开文件,fclose函数来关闭文件
函数fopenfclose在头文件<stdio.h>中定义

//打开文件
FILE *fopen( const char *filename, const char *mode );//关闭文件
int fclose( FILE *stream );

mode表示文件的打开模式,下面是文件的常见打开模式:
文件访问
模式字符串	含义	解释	若文件已存在的动作	若文件不存在的动作
"r"	读	打开文件以读取	从头读	打开失败
"w"	写	创建文件以写入	销毁内容	创建新文件
"a"	后附	后附到文件	写到结尾	创建新文件
"r+"	读扩展	打开文件以读/写	从头读	错误
"w+"	写扩展	创建文件以读/写	销毁内容	创建新文件
"a+"	后附扩展	打开文件以读/写	写到结尾	创建新文件


2.32 文件在当前的工程目录底下

开始时当前工程目录底下没有test.txt这个文件
在这里插入图片描述

运行下面的代码:

#include <stdio.h>int main()
{//打开文件//成功返回有效指针,失败返回NULLFILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//写文件//关闭文件fclose(pf);pf = NULL;return 0;
}

请添加图片描述

在运行上面的代码前我们的文件夹中是没有test.txt这个文件的,当我们运行结束后文件夹中就出现了这么一个文件,并且大小为0


2.33 文件在当前工程目录底下的上一级路径

如果fopen函数操作的文件不在当前工程目录底下,我们需要在文件的前面加上文件位置,如果有转义字符需要用转义转义字符‘\’处理

假如我们就在当前工程目录底下的上一级路径下创建一个文件,里面存入一些数据
请添加图片描述

运行下面的代码:

#include <stdio.h>int main()
{//打开文件//成功返回有效指针,失败返回NULLFILE* pf = fopen(".\\..\\test.txt", "w");//                 相对路径//. 表示当前路径//.. 表示上一级路径if (pf == NULL){perror("fopen");return 1;}//写文件//关闭文件fclose(pf);pf = NULL;return 0;
}

在这里插入图片描述

可以看到文本内容消失,大小为0


2.34 文件不在当前工程目录底下

如果fopen函数操作的文件不在当前工程目录底下,我们需要在文件的前面加上文件位置,如果有转义字符需要用转义转义字符‘\’处理

我们先在桌面上创建一个文本文档,写入一些数据保存
在这里插入图片描述

运行下面的代码:

#include <stdio.h>int main()
{//打开文件//成功返回有效指针,失败返回NULLFILE* pf = fopen("C:\\Users\\86181\\Desktop\\test.txt", "w");//                绝对路径if (pf == NULL){perror("fopen");return 1;}//写文件//关闭文件fclose(pf);pf = NULL;return 0;
}

查看桌面上的文本文档,发现里面的内容已经被清空
在这里插入图片描述


三、文件的随机读写

3.1 fseek

根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)

fseek函数的原型如下:

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

fseek函数的参数:

  • stream:要修改的文件流
  • offset:相对origin迁移的字符数(可正可负)
  • originoffset所加上的位置,它能拥有下列值之一:SEEK_SET(文件起始位置)、SEEK_CUR(文件指针当前位置)、SEEK_END(文件末尾)

运行下面的代码:

#include <stdio.h>int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//bch = fgetc(pf);printf("%c\n", ch);//c//关闭文件fclose(pf);pf = NULL;return 0;
}

上面代码输出的结果是‘a’,‘b’,‘c’,如果我们想让输出的结果是‘a’,‘b’,‘e’的话,就要使用fseek函数,运行下面的代码:

#include <stdio.h>int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//b//定位文件指针//fseek(pf, 4, SEEK_SET);//fseek(pf, -3, SEEK_END);fseek(pf, 2, SEEK_CUR);ch = fgetc(pf);printf("%c\n", ch);//e//关闭文件fclose(pf);pf = NULL;return 0;
}

请添加图片描述


3.2 ftell

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

ftell函数的原型如下:

long ftell( FILE *stream );

运行下面的代码:

#include <stdio.h>int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//b//定位文件指针//fseek(pf, 4, SEEK_SET);//fseek(pf, -3, SEEK_END);fseek(pf, 2, SEEK_CUR);ch = fgetc(pf);printf("%c\n", ch);//e//输出文件指针相较于文件起始位置的偏移量printf("%ld\n", ftell(pf));//关闭文件fclose(pf);pf = NULL;return 0;
}

请添加图片描述

ftell函数并不复杂,它可以随时返回当前文件指针(光标)的偏移量


3.3 rewind

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

rewind函数的原型如下:

void rewind( FILE *stream );

运行下面的代码:

#include <stdio.h>int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//b//定位文件指针//fseek(pf, 4, SEEK_SET);//fseek(pf, -3, SEEK_END);fseek(pf, 2, SEEK_CUR);ch = fgetc(pf);printf("%c\n", ch);//e//输出文件指针相较于文件起始位置的偏移量printf("%ld\n", ftell(pf));//让光标回到文件的起始位置rewind(pf);printf("%ld\n", ftell(pf));//关闭文件fclose(pf);pf = NULL;return 0;
}

请添加图片描述


总结

  • 本篇文章介绍了为什么要有文件,文件的定义,文件的大致分类,流的概念,以及至关重要的文件指针,通过文件指针对文件的打开和关闭,还有当文件在不同路径下如何操作文件等等,通过本文我们对文件有了基本的认识,下篇文章将继续探讨

相关文章:

  • ARM的异常处理
  • Android 上展示 GIF 动图的方法
  • Stable Diffusion 3 Medium 模型
  • Python 机器学习 基础 之 【常用机器学习库】 scikit-learn 机器学习库
  • UnityAPI学习之延时调用(Invoke)
  • vscode中模糊搜索和替换
  • 如何使用asyncua模块在opcua的Server端添加值为列表的变量?
  • Office 2021 mac/win版:智慧升级,办公新风尚
  • 【大数据】计算引擎:Spark核心概念
  • L50--- 104. 二叉树的最大深度(深搜)---Java版
  • windows11 建立批处理bat文件来删除指定目录下的所有隐藏的文件。
  • mysql_ssl_rsa_setup使用详解
  • 【Mysql】 深入理解MySQL的执行计划
  • 【云原生】创建harbor私有仓库及使用aliyun个人仓库
  • 果园预售系统的设计
  • [LeetCode] Wiggle Sort
  • 78. Subsets
  • ESLint简单操作
  • JavaScript新鲜事·第5期
  • JDK 6和JDK 7中的substring()方法
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Magento 1.x 中文订单打印乱码
  • 初识 beanstalkd
  • 对JS继承的一点思考
  • 机器学习中为什么要做归一化normalization
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 算法-图和图算法
  • 微信开源mars源码分析1—上层samples分析
  • 我感觉这是史上最牛的防sql注入方法类
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • Java总结 - String - 这篇请使劲喷我
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • #pragma data_seg 共享数据区(转)
  • #QT(TCP网络编程-服务端)
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • (2015)JS ES6 必知的十个 特性
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (javascript)再说document.body.scrollTop的使用问题
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • ./和../以及/和~之间的区别
  • .apk 成为历史!
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .Net 应用中使用dot trace进行性能诊断
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .NET8使用VS2022打包Docker镜像