【C语言督学训练营 第二十二天】C语言操作文件

news2025/2/27 2:51:54

文章目录

  • 前言
  • 1.文件操作原理解析
  • 2.文件打开及关闭实战
  • 3.文件读写实战
    • 3.1 fread&fwrite
    • 3.2 fgets&fputs
  • 4.文件位置指针偏移实战

前言

其实本篇博客标题应该是第二十三天,因为督学营讲的是二十三天的内容,至于为什么将第二十三天内容调为二十二天是因为博主大致浏览了第二十二天的笔记,讲的均是与汇编相关的计算机组成原理真题,由于学习计组时间太长导致计组相关知识已经尽数遗忘,在这个时刻如果直接进行真题可能会直接打消学计组的念头(难!!!)所以我决定跳过第二十二天或者等学完计组相关部分再回来总结第二十二天的内容。


今天介绍的内容呢是有关计算机操作系统的知识。
在这里插入图片描述

1.文件操作原理解析

程序执行时就称为进程,进程运行过程中的数据均在内存中。需要存储运算后的数据时,就需要使用文件。这样程序下次启动后,就可以直接从文件中读取数据。(不像我们之前的程序,每次运行都需要手动输入数据)。文件是指存储在外部介质(如磁盘、磁带)上的数据集合。操作系统(Windows、Linux、Mac等)是以文件为单位对数据进行管理的,如下图所示。
在这里插入图片描述
语言对文件的处理方法如下。

  • 缓冲文件系统:系统自动地在内存区为每个正在使用的文件开辟一个缓冲区。用缓冲文件系统进行的输入/输出称为高级磁盘输入/输出。
  • 非缓冲文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。用非缓冲文件系统进行的输入/输出称为低级输入/输出。

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

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

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

struct _iobuf i
	char *_ptr;l/下一个要被读取的字符地址
	int_ont;//剩余的字符,若是输入缓冲区,则表示缓冲区中还有多少个字符未被读取
	char *_base;//缓冲区基地址
	int_flag; //读写状态标志位
	int _file;l/文件描述符int_charbuf;
	int_bufsiz;//缓冲区大小
	char *_tmpfname;
};
typedef struct _iobuf FILE;

FILE*fp;//在main.c 写了FILE*fo;然后ctrl+左键就可以跳转到上面的结构体类型定义位置

fp是一个指向FILE类型结构体的指针变量。可以使fp指向某个文件的结构体变量,从而通过该结构体变量中的文件信息来访问该文件。
Windows 操作系统下的FILE结构体与Linux操作系统,Mac操作系统下的FILE结构体中的成员变量名是不一致的,但是其原理可以互相参考。

2.文件打开及关闭实战

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

FILE*fopen(const char *fname, const char *mode);
在这里插入图片描述

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);

接下来结合一个实例来看看如何打开关闭一个文件。

#include <stdio.h>

//练习文件打开
int main() {
    FILE *fp;//定义一个FILE类型的指针变量
    fp=fopen("file.txt","r+");//打开文件
    if(NULL==fp)//判断文件是否打开失败了
    {
        perror("fopen");//perror帮忙定位失败原因
        return -1;
    }
    char c;
//    c=fgetc(fp);
//    printf("%c\n",c);
//    c=fgetc(fp);
//    printf("%c\n",c);
    while((c=fgetc(fp))!=EOF)//读取文件内的所有内容
    {
        printf("%c",c);
    }
    printf("\n");
    c=fputc('H',fp);
    if(-1==c)
    {
        perror("fputc");
        return -1;
    }
    fclose(fp);//关闭文件
    return 0;
}

在这里插入图片描述

冒号之前的内容是我们写入perror函数内的字符串,冒号之后的内容是perror提示的函数失败原因,注意perror函数必须紧跟失败的函数,如果中间执行了printf这样的打印函数,那么perror 函数将提示Success,也就是没有错误,原因是每个库函数执行时都会修改错误码,一旦函数执行成功,错误码就会被改为零,而 perror函数是读取错误码来分析失败原因的。(perror考研不考,是帮助大家定位文件操作失败原因的)
文件打开成功后,使用fgetc函数可以读取文件的每个字符,然后循环打印整个文件,读到文件结尾时返回EOF,所以通过判断返回值是否等于EOF就可以确定是否读到文件结尾。注意要在自己新建的file.txt文件(和 exe在同级目录,注意查看视频操作),文件中先填写一些内容。

3.文件读写实战

3.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,FILEstream);

其中 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;//存储函数的返回值
    //r+代表以文本方式打开文件
    fp=fopen("file.txt","rb+");
    if(NULL==fp)
    {
        perror("fopen");
        return -1;
    }
//    ret=fwrite(buf,sizeof(char),strlen(buf),fp);//把buf中的字符串写入文件
    char buf1[20]={0};
    ret=fread(buf1,sizeof(char),sizeof(buf1),fp);
    printf("%s\n",buf1);
    fclose(fp);
    return 0;
}

fread和 fwrite 函数既可以以文本方式对文件进行读写,又可以以二进制方式对文件进行读写。以"r+"即文本方式打开文件进行读写时,向文件内写入的是字符串,写完后右键文件,选择属性(如下图所示),会发现大小是12个字节。
在这里插入图片描述

这是因为在文本方式下,向文本文件中写入"\n"时实际存入磁盘的是"\r\n",所有的接口调用都是Windows的系统调用,这是Windows的底层实现所决定的(Mac和 Linux 不会)。当然,以文本方式写入,一定要以文本方式读出,遇到"\r\n"时底层接口会自动转换为"\n",因此用fread函数再次读取数据时,得到的依然是"hello\nworld",共11字节。
如果把fopen 函数中的"r+“改为"rb+”,也就是改为二进制方式,那么当我们向磁盘写入11字节时,磁盘实际存储的就是11字节,如果这时双击打开该文件,那么会发现没有换行,即helloworld是连在一起的,中间没有换行符,原因是txt 文本编辑器必须遇到"\r\n"时才进行换行操作。
在这里插入图片描述

相信读者此时已经理解了文本方式和二进制方式的差异(在Mac和 Linux 操作系统下并不存在这样的问题)。以文本方式下写入"\n"后,磁盘存储的是"\r\n",当然读取时会以"\n"的形式读出"\r\n"。而以二进制方式写入"\n"后,磁盘存储的是"\n"。二者在其他方面没有差异。那么如何避免出错呢?如果是以文本方式写入的内容,那么一定要以文本方式读取;如果是以二进制方式写人的内容,那么一定要以二进制方式读取,不能混用!(大家主要理解文本文件与二进制文件的区别即可)。

#include <stdio.h>

int main() {
    FILE* fp;
    int i=123456;
    int ret;
    fp=fopen("file.txt","rb+");//以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",i);
    fclose(fp);
    return 0;
}

上例中写入整型数,浮点数时,一定要用二进制方式时,需要以"rb+"方式打开文件,二进制方式下内存中存储的是什么,写人文件的就是什么,是一致的。例如,写入整型变量i,其值为123456,内存存储为4字节,即Ox0001E240,那么写入内存的也是4字节。这时双击打开文件看到的是乱码,所以读取时也要用一个整型变量来存储。

3.2 fgets&fputs

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

char *fgets(char str, int num, FILEstream);

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

int fputs(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;
}

使用fgets 函数,我们可以一次读取文件的一行,这样就可以轻松地统计文件的行数,注意,在做一些机试题目时(机试是用 fgets读标准输入,因为gets部分学校机试不可用),用于 fgets
函数的 buf不能过小(buf大于最长行的长度),否则可能无法读取"'\n",导致行数统计出错.fputs 函数向文件中写一个字符串,不会额外写入一个"\n",可以不用 fputs,掌握fwrite即可.

4.文件位置指针偏移实战

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <stdio.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);
    fwrite(str,sizeof(char),len,fp);
    ret=fseek(fp,-5,SEEK_CUR);
    if(ret!=0)
    {
        perror("fseek");
        fclose(fp);
        return -1;
    }
    pos= ftell(fp);
    printf("now pos=%ld\n",pos);
    memset(str,0,sizeof(str));//清空str
    fread(str,sizeof(char),sizeof(str),fp);//读取
    printf("str=%s\n",str);
    return 0;
}

最终的运行效果如图1所示。
我们向文件中写入了"hello \nworld",因为是文本方式,所以总计为12字节,通过fseek函数向前偏移5字节后,用ftell函数得到的位置指针距离文件开头的位置即为7,这时再用fread函数读取文件内容,得到的是"world"。
在这里插入图片描述


在这里插入图片描述

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

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

相关文章

学Pyhton静不下来,看了一堆资料还是很迷茫是为什么

一、前言 最近发现&#xff0c;身边很多的小伙伴学Python都会遇到一个问题&#xff0c;就是资料也看了很多&#xff0c;也花了很多时间去学习但还是很迷茫&#xff0c;时间长了又发现之前学的知识点很多都忘了&#xff0c;都萌生出了想半路放弃的想法。 让我们看看蚂蚁金服的大…

如何构建多域名HTTPS代理服务器转发

在当今互联网时代&#xff0c;安全可靠的网络访问是至关重要的。本文将介绍如何使用SNI Routing技术来构建多域名HTTPS代理服务器转发&#xff0c;轻松实现多域名的安全访问和数据传输。 SNI代表"Server Name Indication"&#xff0c;是TLS协议的扩展&#xff0c;用于…

C语言:选择+编程(每日一练Day6)

目录 ​编辑选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;至少是其他数字两倍的最大数 思路一&#xff1a; 思路二&#xff1a; 题二&#xff1a;两个数组的交集…

推荐适用于不同规模企业的会计软件:选择最适合您企业的解决方案

高效的会计软件不仅可以协助企业进行财务管理&#xff0c;做出科学的财务决策&#xff0c;还可以对企业数字化转型提供助力。不同规模的企业需要根据其特定需求选择适合的会计软件。那么有什么适合不同规模企业的会计软件推荐吗&#xff1f; 小型企业的选择 对于小型企业而言&…

【工具使用】Git的使用

dev代表开发版 1. git clone 命令 通过 git add <name> 对文件进行跟踪&#xff0c;把<name>加入到暂存区 git commit -m XXXXXXX 提交修改并补充XXXXX作为注释 “暂存”状态&#xff1a;出现了一些修改&#xff0c;但是还没有提交 对于Java来说&#xff0c;.cl…

OnePlus Open可折叠手机:规格、价格、发布日期等详细信息汇总!

我们知道OnePlus可折叠手机即将问世,无论它是否被命名为OnePlus Open。我们迫不及待地想让它到来,为该公司再添一根弦,为最好的可折叠手机增添一个新的竞争对手。 OnePlus以前没有生产过任何可折叠产品,但它确实拥有合作伙伴公司Oppo的丰富知识,并可以向三星、摩托罗拉和…

【Liunx】冯诺伊曼体系结构

冯诺伊曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺伊曼体系。 到目前为止&#xff0c;我们所认识的计算机&#xff0c;都是由一个个硬件所组成的。 输入单元&#xff1a;键盘&#xff0c;鼠标&am…

开学买哪种电容笔好?平替电容笔和Apple pencil区别

与苹果原装的Pencil相比&#xff0c;普通的电容笔没有任何的重力压感&#xff0c;只能给人一种倾斜的压感。如果你很少用于在绘画上&#xff0c;那就用一支平替电容笔吧&#xff0c;其的价格还算可以接受。这种电容笔&#xff0c;可以用于办公上&#xff0c;也可以用于学习记录…

D盘满到变红色了怎么清理?简单3招,快速释放内存!

“很奇怪哎&#xff0c;我的电脑c盘空间还很多&#xff0c;但是d盘却变红了。因为d盘太多文件了&#xff0c;我不敢随便进行删除&#xff0c;有什么清理d盘的简单方法吗&#xff1f;希望大家帮帮我&#xff01;” 当D盘&#xff08;或任何磁盘分区&#xff09;存储空间接近极限…

【Flutter】2023 Google 开发者大会给 Fluter 带来了什么

文章目录 一、2023年谷歌开发者大会概览二、Flutter 年度进展三、Dart 年度进展四、Material 3 年度进展五、推荐使用Flutter跨平台开发六、参考资料一、2023年谷歌开发者大会概览 你知道吗,今年的谷歌开发者大会真是令人期待。从5月10日的线上Google I/O大会开始,到9月6日至…

C++day2(笔记整理)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.左值引用 #include <iostream> using namespace std;int main() {int a 100;int &ra a; //定义了变量a的引用racout << &a << endl;cout << "&ra" << &…

2023嵌入式大赛应用赛道全国总决赛圆满收官 AidLux系列作品载誉而归

自2月份正式启动以来&#xff0c;第六届&#xff08;2023&#xff09;全国大学生嵌入式芯片与系统设计竞赛共吸引了507所院校&#xff0c;6107支队伍&#xff0c;共计16675人参赛。 经过6个分赛区激烈的初赛、复赛评审角逐后&#xff0c;大赛总决赛于8月15-18日陆续开展并圆满…

创建延时队列、springboot配置多个rabbitmq

创建延时队列 queue.file_delay_destroy x-dead-letter-exchange: exchange.file_delay_destroy x-message-ttl: 259200000 259200000为3天,1000为1秒创建普通队列 queue.file_destroy创建普通交换机 exchange.file_delay_destroytype选择fanout 交换机绑定普通队列 (图中…

加速乐(__jsl_clearance_s)动态cookie生成分析实战

文章目录 一、写在前面二、抓包分析三、逆向分析 一、写在前面 加速乐&#xff08;JSL&#xff09;是阿里推出的一项反爬虫服务&#xff0c;其生成cookie的原理基于浏览器的行为特征 我们知道普通网站生成cookie是在请求时生成&#xff0c;而它先生成cookie&#xff0c;然后向服…

TM4C123库函数学习(3)---串口中断

前言 &#xff08;1&#xff09;学习本文之前&#xff0c;需要先学习前两篇文章。 &#xff08;2&#xff09;学习本文需要准备好TTL转USB模块。 函数介绍 ROM_GPIOPinConfigure&#xff08;&#xff09; 配置GPIO引脚的复用功能。因为引脚不可能只有一个输出输入作用&#xf…

ol-cesium 暴露 Cesium viewer 对象以及二三维切换、viewer 添加点功能示例

ol-cesium 暴露 Cesium viewer 对象以及二三维切换、viewer 添加点功能示例 核心代码完整代码在线示例 二三维一体化的概念一直都比较火热&#xff0c;虽然大多数都是狭义的概念&#xff0c;但是很多需求方也想要这样的功能。 Openlayers 官方出了一个二三维一体化的工具&…

恭喜发财!

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>恭喜发财</title><link rel"stylesheet" href"css/style.css"> </head><body><section class"co…

leetcode1094. 拼车(差分数组-java)

差分数组 leetcode 1094 拼车差分数组代码演示&#xff1a; 前缀和数组 leetcode 1094 拼车 难度 - 中等 原题链接 - 拼车 车上最初有 capacity 个空座位。车 只能 向一个方向行驶&#xff08;也就是说&#xff0c;不允许掉头或改变方向&#xff09; 给定整数 capacity 和一个数…

解决方案:fatal error: openssl/bio.h: 没有那个文件或目录

出现报错如下&#xff1a; 出现该错误的原因有两个&#xff1a; 没有安装openssl或者libssl-dev库Libssl-dev版本过高&#xff0c;需要降级 一. 没有安装openssl或者libssl-dev库 使用指令安装openssl&#xff1a; 我的是已经安装完成了&#xff0c;所以再把libssl-dev的库也…

进程调度和进程切换——《王道考研》

一、王道书咋说 二、chatgpt咋说 进程调度和进程切换是多道程序操作系统中两个关键的概念&#xff0c;它们在处理多个进程时起着不同的作用。 2.1进程调度是指&#xff1a; 操作系统根据一定的调度算法&#xff0c;从就绪态的进程队列中选择一个进程来占用CPU资源&#xff0…