4. 标准 IO 库
4. 标准 IO 库
- 1. 标准 IO 简介
- 2. FILE 指针
- 3. 标准输入、标准输出和标准错误
- 4. fopen() 和 flose()
- 5. fread() 和 fwrite()
- 6. fseek 定位
- 7. 检查或复位状态
- 7.1 feof()
- 7.2 ferrof()
- 7.3 clearerr()
- 8. 格式化 IO
- 8.1 格式化输出
- 8. 2 格式化输入
- 9. IO 缓冲
- 9.1 文件 IO 的内核缓冲
- 9.2 刷新文件 IO 的内核缓冲区
- 9.2.1 控制文件 IO 内核缓冲的系统调用
- 9.2.1 控制文件 IO 内核缓冲的标志
- 9.3 直接 IO,绕过内核缓冲
- 9.3.1 直接 IO 的对齐限制
- 9.3.2 直接 IO 与普通 IO 对比
- 9.4 stdio 缓冲
- 9.4.1 设置 stdio 缓冲
- 9.4.1.1 setvbuf()
- 9.4.1.2 setbuf()
- 9.4.1.3 setbuffer()
- 9.4.2 缓冲模式
- 9.4.3 刷新 stdio 缓冲区
- 10. 文件描述符和FILE指针互转
1. 标准 IO 简介
标准 IO 库指的式标准 C 库中用于文件 IO 操作相关的一系列库函数的集合,底层是用系统 IO 实现的。但是标准 IO 比系统 IO 具有更好的可移植性,因为不同的操作系统内核提供的系统调用都是不一样的;其次标准 IO 具有更高的效率,因为标准 IO 提供了自己的缓冲区,但是系统 IO 不具备缓冲区。
2. FILE 指针
FILE 指针就类似于系统 IO 的文件描述符,FILE 指针是一个结构体类型,包含了标准 IO 为管理文件所需要的所有信息,包括文件描述符、指向文件缓冲区的指针、缓冲区的长度,当前缓冲区中的字节数以及出错标志等。该类型定义在stdio.h
中
3. 标准输入、标准输出和标准错误
标准输入和标准输出通常指的就是键盘和显示器,标准错误也是通过显示器显示的。
通过标准输入设备与系统进行交互时,进程将从标准输入 (stdin) 文件中获取数据,将正常输出数据输出到标准输出 (stdout) 文件,将错误信息输出到标准错误 (stderr) 文件中。标准输出文件和标准错误文件都对应终端的屏幕,而标准输入文件则对应于键盘。
4. fopen() 和 flose()
#include <stdio.h>
int flose(FILE *stream);// 成功返回0,失败返回-1
FILE *fopen(const char *path, const cchar *mode);// 成功返回FILE文件指针,失败返回NULL
/* 参数:* path: 文件路径,可以是相对路径,也可以是绝对路径* mode: 文件权限,是一个字符串* r:只读* r+:可读可写* w:只写,如果文件存在,就将文件截断为0,否则就创建文件* w+:可读可写方式打开文件,如果存在就截断为0,否则就创建文件* a:只写,默认是追加式写入,如果文件不存在就创建文件* a+:可读可写,以追加的方式写入,如果文件不存在就创建文件* /
// 如果文件不存在就会创建文件,新文件的默认权限是0666
5. fread() 和 fwrite()
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
/* 参数:* ptr:存放读取到的数据或者需要写入的数据的缓冲区* size:读取或写入的数据大小的单位,那么总共数据的大小是size*nmemb* nmemb:读取或写入的数据个数* stream:FILE指针*/
6. fseek 定位
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
// 这里的后两个参数和lseek相同,成功返回0,失败返回-1
long ftell(FILE *stream);
// 获取当前读写位置偏移量
7. 检查或复位状态
fread() 读取数据时,如果返回值小于 nmemb,表示发生了错误或者已经读到了文件末尾,但是不能具体确定是哪种情况,可以通过判断错误标志或 end-of-file 标志来确定具体情况
7.1 feof()
用于测试文件的 end-of-file 标志,如果被设置了,则调用 feof() 函数将返回一个非零值,如果没有被设置就返回0.
#include <stdio.h>
int feof(FILE *stream);
// 当文件的读写位置到结尾时,end-of-file 会被设置
7.2 ferrof()
用于测试文件的错误标志,如果错误标注被设置,返回非零值,否则返回 0.
#include <stdio.h>
int ferror(FILE *stream);
7.3 clearerr()
用于清除 end-of-file 或错误标志,当调用上面两个函数校验完之后,通常需要清除这些标志,避免下次校验时使用到的是上一次的值。对于 end-of-file,除了显示清除外,调用 fseek 会自动清除
#include <stdio.h>
void clearerr(FILE *stream);
8. 格式化 IO
8.1 格式化输出
#include <stdio.h>
int print(const char *format,...);
int fprintf(FILE *stream, const char *format,...);
int dprintf(int fd, const char *format,...);
int sprintf(char *buf, const char *format,...);
int snprintf(char *buf, size_t size, const char *format,...);
// format:格式化控制字符串,用于指定后续的参数如何进行格式转换
fprintf(stderr,"hello world\n");
fprintf(stderr,"%d\n",5);dprintf(STDERR_FILENO,"hello world\n");
dprintf(STDERR_FILENO,"%d\n",5);char buf[100];
sprintf(buf,"hello world\n");
sprintf(buf,"%d",100);// 将整形转换为字符串,并且自动在末尾加上一个终止符// 因为sprinf可能会发生缓冲区溢出,就引入了snprintf
// size规定了缓冲区的大小,如果写入到缓冲区的字节数大于size,超出的部分就会丢弃
format:
%[flags][width][.precision][length]type
8. 2 格式化输入
#include <stdio.h>
int scanf(const char *format,...);
int fscanf(FILE *stream, const char *format,...);
int sscanf(const char *str, const char *format,...);
int a,b,c;
scanf("%d%d%d", &a&b&c);
// 使用该函数时,进程会被阻塞,直到键盘有数据输入int a2,b2,c2;
fscanf(stdin,"%d%d%d",&a2,&b2,&c2);char *str="5454 hello";
char buf[10];
int a3;
sscanf(str, "%d%s",&a,buf);
format:
%[*][width][ength]type
或%[m][width][ength]type
如果添加了 *,格式化输入函数会按照转换说明的指示读取输入,但是丢弃输入,意味着不需要对转换后的结果进行存储,所以也就不需要提供相应的指针参数。如果添加了 m,就只能与%s、 %c 以及%[一起使用,调用者无需分配相应的缓冲区来保存格式转换后的数据,原因在于添加了 m,这些格式化输入函数内部会自动分配足够大小的缓冲区,并将缓冲区的地址值通过与该格式转换相对应的指针参数返回出来,该指针参数应该是指向char*
变量的指针。随后,当不再需要此缓冲区时,调用者应调用 free() 函数来释放此缓冲区。
9. IO 缓冲
9.1 文件 IO 的内核缓冲
文件 IO 在进行文件读写操作时并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区之间复制数据,也就是说系统调用和磁盘操作不是同步的。当多个线程同时向文件写入数据时,就会将文件存放在缓冲区中,然后只进行依次和磁盘的 IO 操作。
9.2 刷新文件 IO 的内核缓冲区
强制将文件 IO 的内核缓冲区中缓存的数据刷新到磁盘设备中
9.2.1 控制文件 IO 内核缓冲的系统调用
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);// 不是对某个指定的文件数据进行更新,而是刷新所有文件 IO 内核缓冲区
9.2.1 控制文件 IO 内核缓冲的标志
fd=open(filepath,O_WRONLY|O_DSYNC); // 类似在每个write后调用fdatasync函数
fd=ofen(filepath,O_WRONLY|O_SYNC); // 每个write都会自动将文件内容数据和元数据刷新
9.3 直接 IO,绕过内核缓冲
fd=open(filepath,O_WRONLY|O_DIRECT);
9.3.1 直接 IO 的对齐限制
- 应用程序中用于存放数据的缓冲区,其内存起始地址必须以块大小的整数倍进行对齐
- 写文件时,文件的位置偏移量必须是块大小的整数倍
- 写入到文件的数据大小必须是块大小的整数倍
确认块大小指令df -h
查看 Ubuntu 系统的跟文件系统所挂载的磁盘分区,接着sudo tune2fs -l /dev/sda1 | grep "Block size"
9.3.2 直接 IO 与普通 IO 对比
直接 IO 每次都是直接对磁盘发起操作,而普通方式只是将用户空间下的数据拷贝到文件 IO 内核缓冲区中。直接 IO 效率、性能低,只有一些特殊场合用到
9.4 stdio 缓冲
标准 IO 效率比 文件 IO 高的根本就是它维护了自己的缓冲区,减少了和磁盘的交互
9.4.1 设置 stdio 缓冲
9.4.1.1 setvbuf()
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
// 如果 buf 不为 NULL,那么buf指向size大小的内存区域将作为该文件的stdio缓冲区,所以buf应该以动态分配或静态的方式在堆上开辟空间,而不是在栈上的函数内分配局部变量。如果buf为NULL,那么stdio库会自动分配一块空间作为该文件的stdio缓冲区,除非mode配置为非缓冲区模式
/* mode:指定缓冲区的缓冲类型* _IONBF:不对 IO 进行缓冲。每个标准IO函数将立即调用write或read,并且忽略buf和size参数,可以指定为NULL和0,stderr就是这类* _IOLBF:采用行缓冲,遇到换行符才会执行文件IO操作。对于输出流,在输出一个换行符前将数据缓存(除非缓冲区已经被填满), 当输出换行符时,再将这一行数据通过文件 I/O write()函数刷入到内核缓冲区中;对于输入流, 每次读取一行数据。 对于终端设备默认采用的就是行缓冲模式,譬如标准输入和标准输出。* _IOFBF: 采用全缓冲 I/O。 在这种情况下,在填满 stdio 缓冲区后才进行文件 I/O 操作(read、 write)。对于输出流,当 fwrite 写入文件的数据填满缓冲区时,才调用 write()将 stdio 缓冲区中的数据刷入内核缓冲区;对于输入流, 每次读取 stdio 缓冲区大小个字节数据。 默认普通磁盘上的常规文件默认常用这种缓冲模式*/
// size指定缓冲区大小
当 stdio 缓冲区中的数据被刷入到内核缓冲区或被读取之后,这些数据就不会存在于缓冲区中了,数据被刷入了内核缓冲区或被读走了
9.4.1.2 setbuf()
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
执行和 setvbuf 类似的任务
9.4.1.3 setbuffer()
#include <stdio.h>
void setbuffer(FILE *stream, char *buf, size_t size);
和setbuf类似,但是可以指定缓冲的大小
9.4.2 缓冲模式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{// setvbuf(stdout, NULL, _IONBF, 0);// 将stdout设置为无缓冲printf("Hello World!\n");printf("Hello World!");for ( ; ; )sleep(1);
}
上面的代码只能看到第一个打印信息,第二个看不到,因为第一个是行缓冲,而第二个是全缓冲,只有当程序结束会刷新缓冲区。如果去掉注释,那么两个都可以打印出来。
9.4.3 刷新 stdio 缓冲区
#include <stdio.h>
int fflush(FILE *stream); // 刷新缓冲区
int fclose(FILE *stream); // 关闭缓冲区
强制刷新缓冲区,如果 stream 为 NULL,就表示刷新所有 stdio 缓冲区。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{printf("Hello World!\n");printf("Hello World!");fflush(stdout);// fclose(stdout);for ( ; ; )sleep(1);
}
上面代码一样可以看到两个打印信息。同样,关闭文件也可以刷新缓冲区
10. 文件描述符和FILE指针互转
有时需要将文件 IO 和标准 IO 混合使用
#include <stdio.h>
int fileno(FILE *stream);
FILE *fdopen(int fd, const char *mode);
当混合使用时,需要注意缓冲的问题,文件 IO 会直接将数据写入到内核缓冲区进行高速缓存,而标准 IO 会将数据写入到 stdio 缓冲区,之后再调用 write() 将 stdio 缓冲区中的数据写入到内核缓冲区。比如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{printf("print");write(STDOUT_FILENO,"write\n",6);exit(0);
}
// 这里先输出了write的内容,然后再输出print的内容,因为print没有遇到换行符,也就是清除缓冲区,只有等到运行结束之后才清理
相关文章:

4. 标准 IO 库
4. 标准 IO 库 1. 标准 IO 简介2. FILE 指针3. 标准输入、标准输出和标准错误4. fopen() 和 flose()5. fread() 和 fwrite()6. fseek 定位7. 检查或复位状态7.1 feof()7.2 ferrof()7.3 clearerr() 8. 格式化 IO8.1 格式化输出8. 2 格式化输入 9. IO 缓冲9.1 文件 IO 的内核缓冲…...

SAP Smartform小结
SAP系统做打印单据用的, 感觉很不好用, 特别是要嵌入韩文时必须使用嵌入的word编辑器,运行速度简直不可忍受. 见过一些Adobe interactive form的示例, 看着相当不错, 不过据说需要花money额外买licence, 哪有smartform这种免费东西来得实惠. 一般打印需求,会要求有标题抬头,打…...

KVM虚拟机的NAT网络模式原理及过程展示
NAT的方式及原理 NAT方式是KVM安装后的默认方式。 它支持主机与虚拟机的互访,同时也支持虚拟机访问互联网,但不支持外界访问虚拟机。 default是宿主机安装虚拟机支持模块的时候自动安装的。 其中 virbr0是由宿主机虚拟机支持模块安装时产生的虚拟网络接…...

亚马逊云科技向量数据库助力生成式AI成功落地实践探秘(一)
随着大语言模型效果明显提升,其相关的应用不断涌现呈现出越来越火爆的趋势。其中一种比较被广泛关注的技术路线是大语言模型(LLM)知识召回(Knowledge Retrieval)的方式,在私域知识问答方面可以很好的弥补通…...
C# MemoryCache的使用和封装
封装个缓存类,方便下次使用。 using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic;namespace Order.Core.API.Cache {public class GlobalCache C#有偿Q群:927860652{private static readonly MemoryCache …...
【nlp】4.2 nlp中标准数据集(GLUE数据集合中的dev.tsv 、test.tsv 、train.tsv)
nlp中标准数据集 1 GLUE数据集合介绍1.1 数据集合介绍1.2 数据集合路径2 GLUE子数据集的样式及其任务类型2.1 CoLA数据集文件样式2.2 SST-2数据集文件样式2.3 MRPC数据集文件样式2.4 STS-B数据集文件样式2.5 QQP数据集文件样式2.6 (MNLI/SNLI)数据集文件样式2.7 (QNLI/RTE/WNLI…...
Java LinkedList
LinkedList 一个双向链表。 本身是基于链表进行封装的列表, 所以具备了链表的特性: 变更简单, 容量是无限的, 不必像数组提前声明容量等。 同时 LinkedList 支持存储包括 null 在内的所有数据类型。 1 链表 了解 LinkedList 之前, 我们需要先了解一下双向链的特点 单链表, 双…...

【单片机学习笔记】STC8H1K08参考手册学习笔记
STC8H1K08参考手册学习笔记 STC8H系列芯片STC8H1K08开发环境串口烧录 STC8H系列芯片 STC8H 系列单片机是不需要外部晶振和外部复位的单片机,是以超强抗干扰/超低价/高速/低功耗为目标的 8051 单片机,在相同的工作频率下,STC8H 系列单片机比传统的 8051约快12 倍速度…...
RevCol:可逆的柱状神经网络
文章目录 摘要1、简介2、方法2.1、Multi-LeVEl ReVERsible Unit2.2、可逆列架构2.2.1、MACRo设计2.2.2、MicRo 设计2.3、中间监督3、实验部分3.1、图像分类3.2、目标检测3.3、语义分割3.4、与SOTA基础模型的系统级比较3.5、更多分析实验3.5.1、可逆列架构的性能提升3.5.2、可逆…...

HCIA-RS基础-RIP路由协议
前言: RIP路由协议是一种常用的距离矢量路由协议,广泛应用于小规模网络中。本文将详细介绍RIP路由协议的两个版本:RIPv1和RIPv2,并介绍RIP的常用配置命令。通过学习本文,您将能够掌握RIP协议的基本原理、RIPv1和RIPv2的…...

虚拟化逻辑架构: LBR 网桥基础管理
目录 一、理论 1.Linux Bridge 二、实验 1.LBR 网桥管理 三、问题 1.Linux虚拟交换机如何增删 一、理论 1.Linux Bridge Linux Bridge(网桥)是用纯软件实现的虚拟交换机,有着和物理交换机相同的功能,例如二层交换&#…...
【Spring之AOP底层源码解析,持续更新中~~~】
文章目录 一、动态代理1.1、ProxyFactory1.2、Advice的分类1.3、Advisor的理解 二、创建代理对象的方式2.1、ProxyFactoryBean2.2、BeanNameAutoProxyCreator2.3、DefaultAdvisorAutoProxyCreator 三、Spring AOP的理解3.1、AOP中的概念3.2、Advice在Spring AOP中对应API3.3、T…...

C语言:有一篇文章,共三行文字,每行有80个字符。要求分别统计出单词个数、空格数。
分析: #include<stdio.h>:这是一个预处理指令,将stdio.h头文件包含到程序中,以便使用输入输出函数。 int main():这是程序的主函数,是程序执行的入口点。 char a[3][80];:定义了一个二维…...

【数据结构与算法篇】一文详解数据结构之二叉树
树的介绍及二叉树的C实现 树的概念相关术语树的表示 树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一 个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树, 也就是说它是根朝上,而叶朝…...
Windows主机信息收集命令
一.常用信息搜集 whoami # 查看当前用户 net user # 查看所有用户 query user # 查看当前在线用户 ipconfig /all # 查看当前主机的主机名/IP/DNS等信息 route print # 查看路由表信息 netstat -ano # 查看端口开放情况 arp -a # 查看arp解析情况 tasklist /svc # 查看进…...
「go module」一文总结 go mod 入门使用
文章目录 什么是 Go Modules为什么要使用 Modules怎么使用前置条件项目初始化如何安装/管理依赖?依赖安装 go get版本选择方式 替换版本 replace间接依赖 && go mod tidy远程代理 总结 什么是 Go Modules Module 是 Go 的依赖管理工具。 核心概念 Module…...
48. 旋转图像 --力扣 --JAVA
题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 解题思路 顺时针旋转90度 上下翻转 对角线翻转;两次两层循环…...
Java中的jvm——面试题+答案(Java虚拟机更深层次的概念和原理,包括字节码、代理、内存管理、并发等)——第17期
什么是即时编译(JIT Compilation)? 答案: 即时编译是一种在运行时将字节码转换为本地机器代码的技术,以提高程序的执行速度。JVM中的JIT编译器负责执行这个过程。 什么是Java字节码?为什么Java使用字节码…...

docker打包前端镜像
文章目录 一、构建镜像二、查看本地镜像三、启动容器四、查看启动的容器五、保存镜像六、读取镜像七、创建镜像八、最后 docker官网 一、构建镜像 -t是给镜像命名,.(点)是基于当前目录的Dockerfile来构建镜像 docker build -t image_web .二、查看本地镜像 docke…...

深入理解数据结构:链表
文章目录 🌰导语🌰链表的定义及基本结构🌰单链表🥕单链表特点 🌰双向链表🥕双链表特点 🌰循环链表🥕循环链表特点 🌰链表的操作🍆链表的插入🫘链头…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...