文件(c语言文件流)

embedded/2025/1/31 14:33:56/

前言

什么是文件,文件就是存储在磁盘(硬盘)的文件。在程序设计中,我们一般谈的文件有两种:程序文件、数据文件文件功能的角度来分类

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

根据数据的组织形式,数据文件被分为文本文件和二进制文件。

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

一个数据在文件中是怎么存储的呢?

字符一律以ASCll形式存储,数值型数据既可以用ASCll形式存储,也可以使用二进制形式存储。如有整数10000,如果以ASCll码的形式存储到磁盘上,则此盘占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上值占4个字节。(具体用ASCll码形式还是用二进制形式并没有严格规定)

如上图,以vs举例,通过以二进制形式打开文件并写入了一个10000的数据,4个字节,通过二进制编译器打开他的16进制(32位小端字节序)本来是00 00 27 10 ,反过来就是10 27 00 00。

文件的打开和关闭 

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

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

标准流

什么是标准流,一般就是指键盘,屏幕这些。那我们为什么在写一般的c程序时并没有打开标准流的操作呢,就只是展开头文件呢?

那是因为c语言程序在启动的时候,默认打开了3个流:

stdin - 标准输入流,大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据

stdout - 标准输出流,大多数环境中输出到哦显示器界面,printf函数就是将信息输出到标准输出流中的

stderr - 标准错流,大多数环境中输出到哦显示器界面

这是默认打开的三个流,我们使用的scanf、printf、等函数就可以直接进行输入输出操作的。

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

读和写介绍,我们一般把从外部设备(文件)读取数据并存储到内存中叫读。反之,把从内存中读取数据并存储到文件中叫写。

文件指针

文件指针是一个指向文件的抽象指针,用于标识文件中的当前位置。在C语言中,文件指针的类型是FILE*,他是一个结构体指针,包含了文件的相关信息(如文件描述符、缓冲区、当前位置等)。它描述一个文件信息区,也就是说每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息,也就是这个结构体指针FILE*。但是,不同的编译器FILE类型包含的内容不完全相同,也就是说定义有一定的差异。但也只是大同小异。每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不用很关心细节。一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更家方便。

文件的打开和关闭

文件在读写之前应该先打卡文件,在使用结束后应该关闭文件。在打开文件的时候,会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。

ANSLC规定使用fopen函数来打开文件,fclose函数关闭文件。

mode表示文件的打开模式,如下

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

注意文件打开的前提是,在你的目录中由文件,如果没有,不同的打开方式处理方式不同,有的会报错,有的则会直接给你新建一个文件。

文件的顺序读写函数

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

上面说的适用于所有输入流一般指适用于标准输入流和其他输出流(如文件输入流);所有输出流也是一样的。

具体代码

#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("text.txt", "wb");fwrite(&a, 4, 1, pf);fclose(pf);pf = NULL;
}

文件的随机读取函数

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

#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("text.txt", "wb");fputs("This is an apple.", pf);fseek(pf, 9, SEEK_SET);fputs(" sam", pf);fclose(pf);pf = NULL;
}

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

#include <stdio.h>
int main()
{FILE* pf = fopen("text.txt", "rb");long size;if (pf == NULL)perror("open file");else{fseek(pf, 0, SEEK_SET);size = ftell(pf);fclose(pf);pf = NULL;printf("%ld", size);}return 0;
}

3.rewind:让文件指针的位置回到文件的起始位置

#include <stdio.h>
int main()
{int x;FILE* pf;char buffer[27];pf = fopen("myfile_txt", "w+");for (x = 'A'; x <= 'Z'; x++)fputc(x, pf);rewind(pf);fread(buffer, 1, 26, pf);fclose(pf);pf = NULL;buffer[26] = '\0';printf("%s", buffer);return 0;
}

文件读取结束的判定

被错误使用的feof函数

在文件读取过程中,不能使用feof函数的返回值直接判断文件是否结束。

feof函数的作用是:当文件读取结束的时候,判断读取结束的原因是否:遇到文件尾结束。

文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)

例如:

1. fgetc 判断是否为EOF

2. fgets 判断返回值是否为NULL

#include <stdio.h>
int main()
{int c;  //一定要int ,fgetc的返回值类型是int,非char,要处理EOF,FILE* pf = fopen("text.txt", "r");if (!pf){perror("File opening failed");return 1;}//fgetc当读取失败或读取遇到文件结束的时候,都会返回EOFwhile ((c = fgetc(pf)) != EOF){putchar(c);}//判断什么原因结束if (ferror(pf)){puts("error when reading");}else if(feof(pf)){puts("End of file reached successfully");}return 0;
}

二进制文件的读取结束判断,判断返回值是否小于实际要读的个数

例如:

fread判断返回值是否小于实际要读的个数

#include <stdio.h>int main()
{enum{SIZE = 5};double a[SIZE] = { 1.,2.,3.,4.,5. };FILE* pf = fopen("text.txt", "wb");fwrite(a, sizeof* a, SIZE, pf);fclose(pf);pf = NULL;double b[SIZE];pf = fopen("text.txt", "rb");size_t ret_code = fread(b, sizeof *b, SIZE, pf);if (ret_code == SIZE){puts("Array read successfuly");for (int x = 0; x < SIZE; x++){printf("%f ", b[x]);}putchar('\n');}else if (feof(pf)){printf("Error reading text.txt:unexpected end of file\n");}else if (ferror(pf)){perror("Error reading text.txt");}fclose(pf);pf = NULL;return 0;
}

文件缓存区

ANSI C标准采用缓冲文件系统处理数据文件,所谓的缓冲文件系统是指系统自动地在内存中位程序中每一个正在使用的文件开辟一块文件缓冲区。从内存向磁盘输出数据会先送到内存中的缓存区,装满缓冲区才一起送到磁盘上。如果从磁盘像计算及读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定。

为什么存在文件缓存区

提高读写速度,简单来说缓存区是属于内存上的一块区域,当需要从磁盘上读取数据时,先读取到缓存中,从缓存中读取到CPU上是很快的,这是因为磁盘和CPU是两个单独设备,这两个不同设备从对方中读取是比较慢的,而且很多时候需要重复读取数据,这时候缓冲区的存在就大大减少了重复读写的开销,要注意缓冲区属于内存上的一块区域,它并不属于一块独立的外部设备。这一点对于计算机的爱好者应该是了如指掌。但是,对于初入计算机的学习者需要加强一下了解。


http://www.ppmy.cn/embedded/158377.html

相关文章

python flask 使用 redis写一个例子

下面是一个使用Flask和Redis的简单例子&#xff1a; from flask import Flask from redis import Redisapp Flask(__name__) redis Redis(hostlocalhost, port6379)app.route(/) def hello():# 写入到Redisredis.set(name, Flask Redis Example)# 从Redis中读取数据name re…

虚幻基础10:isValid

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 isValid isValid 节点&#xff1a;检测资产&#xff0c;防止游戏崩溃。

rsync安装与使用-linux015

使用 rsync 可以非常高效地将文件或目录从一个服务器传输到另一个服务器。 能力&#xff1a; 支持 64 位文件、64 位 inode、64 位时间戳、64 位长整型支持套接字对、符号链接、符号链接时间、硬链接、硬链接特殊文件、硬链接符号链接支持 IPv6、访问时间&#xff08;atimes&…

【某大厂一面】ThreadLocal如何实现主子线程之间的数据同步

ThreadLocal 是 Java 中用于实现线程本地存储的类&#xff0c;它为每个线程提供独立的变量副本&#xff0c;确保线程间的数据隔离。然而&#xff0c;ThreadLocal 本身并不直接支持主子线程之间的数据同步。要实现主子线程之间的数据同步&#xff0c;可以结合 InheritableThread…

langchain基础(二)

一、输出解析器&#xff08;Output Parser&#xff09; 作用&#xff1a;&#xff08;1&#xff09;让模型按照指定的格式输出&#xff1b; &#xff08;2&#xff09;解析模型输出&#xff0c;提取所需的信息 1、逗号分隔列表 CommaSeparatedListOutputParser&#xff1a;…

记录一个连不上docker中的mysql的问题

引言 使用的debian12,不同发行版可能有些许差异&#xff0c;连接使用的工具是navicat lite 本来是毫无思绪的&#xff0c;以前在云服务器上可能是防火墙的问题&#xff0c;但是这个桌面环境我压根没有使用防火墙。 直到 ying192:~$ mysql -h127.0.0.1 -uroot ERROR 1045 (28…

【精选】基于数据挖掘的招聘信息分析与市场需求预测系统 职位分析、求职者趋势分析 职位匹配、人才趋势、市场需求分析数据挖掘技术 职位需求分析、人才市场趋势预测

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

汇编的使用总结

一、汇编的组成 1、汇编指令&#xff08;指令集&#xff09; 数据处理指令: 数据搬移指令 数据移位指令 位运算指令 算术运算指令 比较指令 跳转指令 内存读写指令 状态寄存器传送指令 异常产生指令等 2、伪指令 不是汇编指令&#xff0c;但是可以起到指令的作用&#xff0c;伪…