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没有遇到换行符,也就是清除缓冲区,只有等到运行结束之后才清理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

坚鹏:中国人寿临沂公司当下中国经济形势与寿险业发展机遇培训

中国人寿保险&#xff08;集团&#xff09;公司属国家大型金融保险企业&#xff0c;2016年中国人寿入主广发银行&#xff0c;开启保险、投资、银行三大板块协同发展新格局。2022年&#xff0c;集团公司合并营业收入站稳万亿平台&#xff1b;合并总资产突破6万亿元大关。中国人寿…

数据结构与算法Java版本单元测验题

1.【实验题 2-2】实现以下对单链表的操作&#xff0c;题意和算法描述见《习题解答》图 2-7。 //将单链表 list 逆转&#xff0c;将各结点的 next 指向其前驱。泛型方法&#xff0c;返回值类型前声明类型参数 T public static void reverse(SinglyList list) 【思考题 2-6】实现…

MySQL进阶知识

目录 MySQL的Linux安装 存储引擎 MySQL的体系结构 存储引擎简介 存储引擎特点 InnoDB 逻辑存储结构 MyISAM Memory 对比 存储引擎选择 索引 介绍 索引结构 BTree索引 Hash索引 索引分类 索引语法 SQL性能分析 SQL执行频率 慢查询日志 profile详情 expla…

VUE简易购物车程序

目录 效果预览图 完整代码 效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

【数据结构初阶】树,二叉树

树&#xff0c;二叉树 1.树概念及结构1.1树的概念1.2 树的相关概念1.3 树的表示1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树概念及结构2.1概念2.2现实中的二叉树2.3 特殊的二叉树2.4 二叉树的性质2.5 二叉树的存储结构 1.树概念及结构 1.…

开发知识点-ArkTS-鸿蒙开发-Typescript

Typescript IED IED https://developer.harmonyos.com/cn/develop/deveco-studio/#download

我的第一次SACC之旅

今年有很多第一次&#xff0c;第一次作为“游客”参加DTCC&#xff08;中国数据库大会&#xff09;&#xff0c;第一次作为讲师参与ACDU中国行&#xff08;成都站&#xff09;&#xff0c;第一次参加OB年度发布会&#xff08;包含DBA老友会&#xff09;&#xff0c;而这次是第一…

弹窗concrt140.dll丢失的解决方法,深度解析concrt140.dll丢失的原因

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示或者系统崩溃的情况。其中&#xff0c;concrt140.dll是一个常见的错误提示&#xff0c;这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;本文将介绍5种详细的解决方法&#xff0c;帮助您恢…

SpringBoot进阶——解释springboot的自动配置原理

相关的博客文章如下&#xff1a; SpringBootApplication注解的理解——如何排除自动装配 & 分布式情况下如何自动加载 & nacos是怎么被发现的 引出 1.spring.factories文件存储能够进行自动配置的Bean信息&#xff1b; 2.EnableAutoConfiguration关闭数据源的自动配置…

SpringBoot——自定义start

优质博文&#xff1a;IT-BLOG-CN 一、Mybatis 实现 start 的原理 首先在写一个自定义的start之前&#xff0c;我们先参考下Mybatis是如何整合SpringBoot&#xff1a;mybatis-spring-boot-autoconfigure依赖包&#xff1a; <dependency><groupId>org.mybatis.spr…

【Linux】一文看懂基础IO并模拟实现

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0. C语言的文件接口1. 系统的文件接口1.1 open打开文件1.2 write写入文件 2. 文件系统介绍2.1 如何理解一切皆文件? 3. 输入输…

Pyqt5设计师中如何插入图片

问题描述&#xff1a;Pyqt5设计师中如何插入图片。使用Pyqt5做一个示意图界面&#xff0c;是一个”假界面“。 问题解决&#xff1a; 第一步&#xff0c;从Widget Box中拖入一个Label&#xff0c;具体如下图所示。 第二步&#xff0c;在右侧属性编辑器→QLabel→pixmap中选择…

从 CoT 到 Agent,最全综述来了!上交出品

就在前两天&#xff0c;我们刚刚和大家聊了聊最近相当火爆的 AI Agents 这一概念&#xff1a;聊聊我对AI Agents技术的一些看法。水平所限&#xff0c;我们也只是浅浅为大家梳理了一下 AI Agents 的概念发展与其代表性技术&#xff0c;一来不深入二来不细致&#xff0c;只能供大…

【Web-Note】 JavaScript概述

JavaSript基本语法 JavaSript程序不能独立运行&#xff0c;必须依赖于HTML文件。 <script type "text/javascript" [src "外部文件"]> JS语句块; </script> script标记是成对标记。 type属性&#xff1a;说明脚本的类型。 "text/jav…

机器学习库:numpy

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 文章目录 写在开头 基本数据格式 array 数据定位 argmax 数据生成 random.rand random.randn random.randint 维度拓展 expand_dim 结语 写在…

python桌面开发PyQt6库和工具库QTDesigner安装和配置

一、安装PyQt6 二、安装pyqt6-tools 三、安装外部工具 四、创建QTDesigner 1.首先查找designer.exe的路径&#xff08;可以自己在窗口中选择&#xff0c;也可以使用Everything搜索&#xff09; 2.使用Everything搜索后会出现多个designer.exe,选中&#xff0c;OpenPath 3.选择…

单片机学习7——定时器/计数器编程

#include<reg52.h>unsigned char a, num; sbit LED1 P1^0;void main() {num0;EA1;ET01;//IT00;//设置TMOD的工作模式TMOD0x01;//给定时器装初值&#xff0c;50000,50ms中断20次&#xff0c;就得到1sTH0(65536-50000)/256;TL0(65536-50000)%256;TR01; // 定时器/计数器启…

【Spring Boot】如何集成Swagger

Swagger简单介绍 Swagger是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化RESTful风格的Web服务。功能主要包含以下几点&#xff1a; 可以使前后端分离开发更加方便&#xff0c;有利于团队协作接口文档可以在线自动生成&#xff0c;有利于降低后端开发人员编写…

二叉树经典面试题—折纸

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 1 折纸问题1.1 解决思路1.2 实现代码 &#x1f48e;总结 1 折纸问题 1.1 解决思路 请把一段纸条竖着放在桌子上&#xff0c;然后从纸条的下边向上方对折1次&#xff0c;压出折痕后展开。此时折痕是凹下去的&#xf…

包你学会十大内排序算法,广度深度兼备,动图演示,详细源码

内排序算法合集 文章目录 内排序算法合集前言冒泡排序冒泡排序的实现冒泡排序的简单实现冒泡排序的优化版本冒泡排序的复杂度分析 简单选择排序简单选择排序的实现简单选择排序的复杂度分析 直接插入排序直接插入排序的实现直接插入排序的复杂度分析 希尔排序希尔排序原理希尔排…
最新文章