【408篇】C语言笔记-第二十二章(文件的操作)

news2024/12/25 13:25:40

文章目录

    • 第一节:文件操作原理
      • 1. C文件概述
      • 2. 文件指针介绍
    • 第二节:文件的打开与关闭
      • 1. 文件打开与关闭常用函数
      • 2. 代码实战
    • 第三节:文件的读写
      • 1. fread函数与fwrite函数
      • 2. fgets函数与fputs函数
    • 第四节:文件位置指针偏移
      • 1. fseek函数
      • 2. ftell函数

第一节:文件操作原理

1. C文件概述

程序执行时就称为进程,进程运行过程中的数据均在内存中。需要存储运算后的数据时,就需要使用文件。这样程序下次启动后,就可以直接从文件中读取数据。

文件是指存储在外部介质(如磁盘、磁带)上的数据集合。操作系统是以文件为单位对数据进行管理的。

说明:输入和输出缓冲区实际上是同一个,这里为了形象化表示而分开了。

C语言对文件的处理方法如下:

缓冲文件系统:系统自动地在内存中为每个正在使用的文件开辟一个缓冲区。用缓冲区文件系统进行的输入/输出称为高级磁盘输入/输出。

非缓冲区文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。用非缓冲区文件系统进行的输入/输出称为低阶输入/输出。

缓冲区原理:

缓冲区其实就是一段内存空间,分为读缓冲、写缓冲。C语言缓冲的三种特性如下:

  • 全缓冲。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写操作。
  • 行缓冲。在这种情况下,当在输入和输出中遇到换行符时,将执行真正的I/O操作,这是,我们输入的字符先放到缓冲区中,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入缓冲区(stdin)和标准输出缓冲区(stdout)。
  • 不带缓冲。也就是不进行缓冲,标准出错情况(stderr)是典型代表,这使得出错信息可以直接尽快地显示出来。

2. 文件指针介绍

打开一个文件后,我们会得到一个FILE*类型的文件指针fp,然后通过该文件指针对文件进行操作。FILE是一个结构体类型,源码如下:

  struct _iobuf {
    char *_ptr; // 下一个要被读取的字符地址
    int _cnt;  // 剩余的字符,若是输入缓冲区,则表示缓冲区中还有多少个字符未被读取
    char *_base; // 缓冲区基地址
    int _flag;  // 读写状态标志位
    int _file;  // 问价描述符
    int _charbuf;
    int _bufsiz;  // 缓冲区大小
    char *_tmpfname;
  };
  typedef struct _iobuf FILE;

说明:fp是一个指向FILE类型结构体的指针变量。可以使fp指向某个文件的结构体变量,从而通过该结构体变量中的文件信息来访问该文件

第二节:文件的打开与关闭

1. 文件打开与关闭常用函数

fopen函数用于打开由fname(文件名)指定的文件,并返回一个关联该文件的流。如果发生错误,那么fopen返回NULL,mode(方式)用于决定文件的用途(如输入、输出等),具体形式如下:

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

常用的mode参数及其各自的意义如下所示:

mode(方式)意义
r打开一个用于读取的文本文件
w创建一个用于写入的文本文件,如果存在会清空文件
rb打开一个用于读取的二进制文件
wb创建一个用于写入的二进制文件,如果存在会清空文件
r+打开一个用于读/写的文本文件
w+创建一个用于读/写的二进制文件,如果存在会清空文件
rb+打开一个用于读/写的二进制文件
wb+创建一个用于读/写的二进制文件,如果存在会清空文件

fclose函数用于关闭给出的文件流,并释放已关联到的流的所有缓冲区。fclose执行成功时返回0,否则返回EOF。具体形式如下:

int fclose(FILE *stream);

fputc函数用于将字符ch的值输出到fp指向的文件中,如果输出成功,那么返回输出的字符;如果输出失败,那么返回EOF,具体形式如下:

int fputc(int ch,FILE *stream)

fgetc函数用于从指定的文件中读入一个字符,该文件必须是以读或写方式打开的。如果读取一个字符成功,那么赋给ch。如果遇到文件结束符,那么返回文件结束标志EOF,具体形式如下:

int fgetc(FILE *stream);

2. 代码实战

#include <stdio.h>

int main() {
    FILE *fp;  // 文件类型指针
    int i;
    char c;
    fp= fopen("file.txt","r+");
    if(NULL==fp){
        perror("fopen");
        return -1;
    }
    while ((c= fgetc(fp))!=EOF){
        printf("%c",c);
    }
    printf("\n");
    i= fputc('H',fp);
    if(EOF==i){
        perror("fputc");
    }
    fclose(fp);
    return 0;
}
# file.txt
helloworld
F:\Computer\Project\practice\22\22.2-File\cmake-build-debug\22_2_File.exe
helloworld

进程已结束,退出代码为 0

当执行完fputc函数后,实际file.txt的内容为:

helloworldH

说明:perror函数得到打开失败的原因(对于定位函数失败的原因,常用perror函数)。如果未新建一个文件,即文件不存在,那么会出现如下如所示的失败提示。

注意:perror函数必须紧跟着失败的函数。因为perror函数时读取错误码来分析失败原因的,执行了其他函数错误码会被改为零。

文件打开成功后,使用fgetc函数可以读取文件的每个字符,然后循环打印整个文件,读到文件结尾时返回EOF,所以通过判断返回值是否等于EOF就可以确定是否读到文件结尾。注意要在自己新建的file.txt文件中先填写一些内容

第三节:文件的读写

1. fread函数与fwrite函数

fread函数与fwrite函数具体形式如下:

int fread(void *buffer,size_t size,size_t num,FILE *stream)int fwrite(const void *buffer,size_t size,size_t count,FILE *stream);

其中buffer是一个指针,对fread来说它是读入数据的存放地址,对fwrite来说它是输出数据的地址(均指起始地址);size是要读写的字节数;count是要进行读写多少size字节的数据项;fp是文件型指针;fread函数的返回值是读取的内容数量,fwrite写成功后的返回值是已写对象的数量

#include <stdio.h>
#include <string.h>

int main() {
    char buf[20]="hello\nworld";
    FILE *fp;
    int ret;
    fp= fopen("file.txt","r+");  // 或者写成rb+
    if(NULL==fp){
        perror("fopen");
        return -1;
    }
    // fwrite和fread不要同时执行
//    ret= fwrite(buf,sizeof(char), strlen(buf),fp); // 把buf中的字符写入文件
    ret= fread(buf, sizeof(char), sizeof(buf)-1,fp);
    puts(buf);
    fclose(fp);
    return 0;
}
F:\Computer\Project\practice\22\22.4-fread-fwrite\cmake-build-debug\22_4_fread_fwrite.exe
hello
world

进程已结束,退出代码为 0

fread和fwrite函数既可以以文本方式对文件进行读写,又可以以二进制方式对文件进行读写

说明:以“r+”即文本方式打开文件进行读写时,项文件内写入的是字符串"hello\nworld",会发现是12个字节。

原因:向文本文件中写入"\n"时实际存入磁盘的是"\r\n",这是Windows系统的底层实现所决定的(Mac和Linux不会)。以文本的方式写入,一定要以文本的方式读出

如果以“rb+”二进制方式写入"hello\nworld",会发现还是11个字节。

通过以上差异,我们可以知道:对于字符型数据,如果是以文本方式写入的内容,那么一定要以文本方式读取;如果是以二进制方式写入的内容,那么一定要以二进制方式读取,不能混用

而对于整型数,浮点数,一定要以二进制方式"rb+"方式打开文件,二进制方式下内存中存储的是什么,写入文件的就是什么,是一致的

#include <stdio.h>

int main() {
    FILE *fp;
    int i=123456;
    int ret;
    fp= fopen("file.txt","rb+");
    if(NULL==fp){
        perror("fopen");
        return -1;
    }
    // 向文件中写入整型数,如果我们双击打开文件会发现乱码,因为打开文件都是以字符格式取解析的
//    ret= fwrite(&i, sizeof(int),1,fp);
    i=0;
    fread(&i, sizeof(int),1,fp);
    printf("i=%d\n",i);
    fclose(fp);
    return 0;
}
F:\Computer\Project\practice\22\22.3-int-float\cmake-build-debug\22_3_int_float.exe
i=123456

进程已结束,退出代码为 0

2. fgets函数与fputs函数

函数fgets从给出的文件流中读取[num-1]个字符,并且把它们存储到str(字符串)中。fgets在达到末行时停止,fgets成功时返回str(字符串),失败时返回NULL,读到文件结尾时返回NULL。具体形式如下:

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

fputs函数把str(字符串)指向的字符写到给出的输出流。成功时返回非负数,失败时返回EOF,具体形式如下:

int puts(const char *str,FILE *stream);
#include <stdio.h>

int main() {
    char buf[20]={0}; // 用于存储读取数据
    FILE *fp;
    fp= fopen("file.txt","r+");
    if(NULL==fp){
        perror("fopen");
        return -1;
    }
    while (fgets(buf, sizeof(buf),fp)!=NULL){ // 读到文件结尾时,fgets返回NULL
        printf("%s",buf); // 打印输出某一行的内容
    }
    return 0;
}
F:\Computer\Project\practice\22\22.4-fgets-fputs\cmake-build-debug\22_4_fgets_fputs.exe
hello
world
how are you

进程已结束,退出代码为 0

使用fgets函数,我们可以一次读取文件的一行,这样就可以轻松地统计文件的行数。

注意:在一些机试题目中,用于fgets函数的buf不能过小,否则可能无法读取"\n",导致统计出错。

fputs函数向文件中写一个字符串,不会额外写入"\n",可以不用fputs,掌握fwrite即可。

第四节:文件位置指针偏移

1. fseek函数

fseek函数的功能是改变文件的位置指针。具体形式如下:

int fseek(FILE *stream,long offset,int origin);
// fseek(文件类型指针,位移量,起始点)

起始点的说明:

  1. 文件开头 SEEK_SET 0
  2. 当前位置 SEEK_CUR 1
  3. 文件末尾 SEEK_END 2

位移量是指以起始点为基点,向前移动的字节数。一般要求为long型。

fseek函数调用成功时返回零,调用失败时返回非零。

2. ftell函数

ftell函数返回stream(流)当前的文件位置,发生错误时返回-1,放我们想知道位置指针距离文件开头的位置时,就需要用到ftell函数,具体形式如下:

long ftell(FILE *stream);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    FILE *fp;
    char str[20]="hello\nworld";
    int len=0; // 用于保存字符串长度
    long pos;
    int ret;
    fp= fopen("file.txt","r+");
    if(NULL==fp){
        perror("fopen");
        return -1;
    }
    len= strlen(str); // 保存字符串长度
    ret= fwrite(str, sizeof(char),len,fp); // 写入字符串到文件中
    ret= fseek(fp,-5,SEEK_CUR); // 从当前位置向前偏移5个字节
    if(ret!=0){
        perror("fseek");
        fclose(fp);
        return -1;
    }
    pos= ftell(fp); // 获取位置指针举例文件开头的距离
    printf("now pos=%ld\n",pos);
    memset(str,0, sizeof(str)); // 把str清空
    ret= fread(str, sizeof(char), sizeof(str),fp); // 这时候会读到字符串world
    printf("%s\n",str);
    fclose(fp);
    return 0;
}
F:\Computer\Project\practice\22\23.5-ftell-fseek\cmake-build-debug\23_5_ftell_fseek.exe
now pos=7
world

进程已结束,退出代码为 0

说明:为什么pos=7,因为Windows多保存了一个"\r"。

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

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

相关文章

2023-01-04 clickhouse-编译、开发、测试

https://bohutang.me/2020/06/05/clickhouse-and-friends-development/ 一次偶然的机会&#xff0c;和ClickHouse团队做了一次线下沟通&#xff0c;Alexey提到ClickHouse的设计哲学: The product must solve actual problemAnd do it better than others 用工程思维解决商业问…

人工智能期末试卷

一、简答题&#xff08;共 24 分&#xff09; 若将人看成一个信息处理系统&#xff0c;1) 人的智能具有哪些特征&#xff1f;2) 举例说明哪一特征是最重要的并 3) 阐述其与实现通用人工智能的关系。(要求&#xff1a;2、3 小问一定用自己的语言作答&#xff01;)&#xff08;8 …

javaweb课程设计-基于SSM框架的疫情数据统计分析系统源码+数据库,可以进行疫情数据录入、疫情数据查询、图表展示

疫情数据统计分析系统 完整代码下载地址&#xff1a;基于SSM框架的疫情数据统计分析系统源码数据库 介绍 疫情数据统计分析系统是一个基于SSM框架的网页端系统&#xff0c;项目中实现的功能如下&#xff1a;用户访问网站可以浏览全国疫情的图表信息&#xff0c;管理员登录后…

P1229 遍历问题

题目描述 我们都很熟悉二叉树的前序、中序、后序遍历&#xff0c;在数据结构中常提出这样的问题&#xff1a;已知一棵二叉树的前序和中序遍历&#xff0c;求它的后序遍历&#xff0c;相应的&#xff0c;已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定…

人工智能知识图谱研究

1、研究背景及意义 随着互联网技术的发展以及大数据、人工智能等新科技时代的来临&#xff0c;我国高校教育改革、高校人才培养也面临着新的机遇与挑战。一方面&#xff0c;为了实现国家战略、支撑快速发展的新经济&#xff0c;需要高校变革发展培养新型人才&#xff0c;满足社…

Au 效果器详解:自适应降噪

Au菜单&#xff1a;效果/降噪/恢复/自适应降噪Adaptive Noise Reduction自适应降噪 Adaptive Noise Reduction可快速去除变化的宽频噪声&#xff0c;如背景声音、隆隆声、风声等。此效果实时起作用&#xff0c;并可在多轨编辑器中使用。相对于标准降噪效果&#xff0c;自适应降…

cc1-7分析-2

cc2 cc2和cc4呢其实区别也不是很大&#xff0c;最后的rce的方式也都是一样的。区别在哪呢&#xff0c;之前我们说过TemplatesImpl.newTransformer是可以直接进行rce的&#xff0c;cc2就是通过 InvokerTransformer直接去调用TemplatesImpl.newTransformer&#xff0c;不走Insta…

Kubernetes(3)- Serivce详解

第七章 Service详解 本章节主要介绍kubernetes的流量负载组件&#xff1a;Service和Ingress。 Service介绍 ​ 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着…

【数据结构】带头双向循环链表的实现

目录 一、什么是带头双向循环链表 二、带头双向循环链表的实现 1、创建一个动态头结点 2、双向链表初始化 3、打印双向链表 4、双向链表尾插 5、双向链表尾删 6、双向链表头插 7、双向链表头删 8、双向链表查找 9、双向链表在pos的前面进行插入x 10、双向链表删除pos位置的结点…

植物大战僵尸:寻找葵花生产速度

通过CE修改器遍历出控制太阳花吐出阳光的时间变量&#xff0c;太阳花吐出阳光是由一个定时器控制的&#xff0c;首先我们找到第一个太阳花的基址与偏移&#xff0c;然后找出第二个太阳花的动态地址&#xff0c;并通过公式计算得到太阳花结构长度的相对偏移&#xff0c;最后我们…

C++ 大漠插件免注册调用

1&#xff1a; 参考文章&#xff1a; https://blog.csdn.net/chuhe163/article/details/1127455902&#xff1a; 免注册调用代码实现2.1 先建一个空的mfc项目2.2 拷贝dm.dll 到文件项目所在文件夹。2.2.1 拷贝到项目后&#xff0c;右键项目->添加 ->现有项 ->选择dm.d…

四、常用注解

文章目录四、常用注解1、TableName1.1 问题1.2 通过TableName解决问题1.3 通过全局配置解决问题2、TableId2.1 问题2.2 通过TableId解决问题2.3 TableId的value属性2.4 TableId的type属性2.5 雪花算法3、TableField3.1 情况13.2 情况24、TableLogic4.1 逻辑删除4.2 实现逻辑删除…

JavaScript 自执行函数防止冲突全局作用域变量 - 在线客服源码实现弹窗效果JavaScript SDK...

当我在实现在线客服源码弹窗效果JavaScript SDK时&#xff0c;对外公开的SDK代码就是使用的自执行函数的形式。 使用自执行函数来实现 JavaScript SDK 有以下好处&#xff1a; 封装代码&#xff1a;自执行函数可以将你的 JavaScript 代码封装起来&#xff0c;从而避免在全局作用…

文件字节输出流、文件拷贝、资源释放的2种方式

文件字节输出流&#xff1a;写字节数据到文件&#xff1a; API: 注意&#xff1a;close() 包含了 flush() ; 关闭后流就不可以继续使用了 写一个字节出去&#xff1a; 98表示一个字节 写一个字节数组&#xff1a; 注意&#xff1a;写数字和字母可以直接写出去&#xff0c;但…

【二分查找】有界数组中指定下标处的最大值

题目描述 给你三个正整数 n、index 和 maxSum 。你需要构造一个同时满足下述所有条件的数组 nums&#xff08;下标 从 0 开始 计数&#xff09;&#xff1a; nums.length nnums[i] 是 正整数 &#xff0c;其中 0 < i < nabs(nums[i] - nums[i1]) < 1 &#xff0c;其…

leetcode 2244. Minimum Rounds to Complete All Tasks(完成所有task至少要多少轮)

tasks数组里面的数字表示难度的等级&#xff0c;每一轮只能完成2 或者 3个同等级的task, 问至少需要多少轮能完成所有的task, 不能完成的返回-1. 思路&#xff1a; 先来看下什么情况下不能完成。 由于一轮只能完成2 或 3个&#xff0c;那如果该等级的task只有一个呢&#xff…

P1-- 信号--通讯原理

前言&#xff1a; 最近看了《无线系统设计与国际标准》后面的几个核心技术 OFDM,Modulation&#xff0c;格雷码&#xff0c;MIMO 等技术&#xff0c;其底层的数学思想主要包括傅里叶变换 &#xff0c;狄拉克函数&#xff0c;卷积&#xff0c;线性代数基础运算。 这边结合 北京…

Electron开发-从推门到进门

一、Electron 的介绍 Electron是利用web前端技术进行桌面应用开发的一套框架。是由 github 开发的开源框架&#xff0c;允许开发者使用 Web 技术构建跨平台的桌面应用&#xff0c;它的基本结构&#xff1a; Electron Chromium Node.js Native API Chromium&#xff1a;为 …

大数据挖掘-伤寒论和金匮要略(COVID-19用药启示录)

来自Toby老师&#xff0c;大数据挖掘-伤寒论和金匮要略 大家好&#xff0c;我是Toby老师&#xff0c;三年来新冠病毒肆虐全球&#xff0c;带来一些列症状&#xff0c;例如发热&#xff0c;恶寒&#xff0c;咳嗽&#xff0c;咽喉痛&#xff0c;腹泻&#xff0c;心脑血管疾病等等…

最低成本尝试做游戏的方式

本文首发于微信公众号&#xff1a;小蚂蚁教你做游戏。欢迎关注领取更多学习做游戏的原创教程资料&#xff0c;每天学点儿游戏开发知识。嗨&#xff01;大家好&#xff0c;我是小蚂蚁。如果说有 100 个人想过去做游戏的话&#xff0c;那么最终大概只有不到 20 个人真的去尝试了&…