Linux--标准IO库

news2024/11/28 22:37:03

一、标准IO简介

所谓标准 I/O 库则是标准 C 库中用于文件 I/O 操作(譬如读文件、写文件等)相关的一系列库函数的集合,通常标准 I/O 库函数相关的函数定义都在头文件 <stdio.h> 中,所以我们需要在程序源码中包含 <stdio.h> 头文件。
标准 I/O 库函数是构建于文件 I/O open() read() write() lseek() close() 等)这些系统调用之上的, 譬如标准 I/O 库函数 fopen() 就利用系统调用 open() 来执行打开文件的操作、 fread() 利用系统调用 read() 来执行读文件操作、fwrite() 则利用系统调用 write() 来执行写文件操作等等。
标准 I/O 和文件 I/O 的区别如下:
虽然标准 I/O 和文件 I/O 都是 C 语言函数,但是标准 I/O 是标准 C 库函数,而文件 I/O 则是 Linux系统调用;
标准 I/O 是由文件 I/O 封装而来,标准 I/O 内部实际上是调用文件 I/O 来完成实际操作的;
可移植性:标准 I/O 相比于文件 I/O 具有更好的可移植性,通常对于不同的操作系统,其内核向应用层提供的系统调用往往都是不同,譬如系统调用的定义、功能、参数列表、返回值等往往都是不 一样的;而对于标准 I/O 来说,由于很多操作系统都实现了标准 I/O 库,标准 I/O 库在不同的操作系统之间其接口定义几乎是一样的,所以标准 I/O 在不同操作系统之间相比于文件 I/O 具有更好的 可移植性。
性能、效率:标准 I/O 库在用户空间维护了自己的 stdio 缓冲区,所以标准 I/O 是带有缓存的,而 文件 I/O 在用户空间是不带有缓存的,所以在性能、效率上,标准 I/O 要优于文件 I/O

二、FILE指针

之前所有文件 I/O 函数( open() read() write() lseek() 等)都是围绕文件描述符进行的, 当调用 open() 函数打开一个文件时,即返回一个文件描述符 fd ,然后该文件描述符就用于后续的 I/O 操作。
而对于标准 I/O 库函数来说,它们的操作是围绕 FILE 指针进行的,当使用标准 I/O 库函数打开或创建一个文件时,会返回一个指向 FILE 类型对象的指针(FILE * ,使用该 FILE 指针与被打开或创建的文件相关联,然后该 FILE 指针就用于后续的标准 I/O 操作(使用标准 I/O 库函数进行 I/O 操作),所以由此可知, FILE 指针的作用相当于文件描述符,只不过 FILE 指针用于标准 I/O 库函数中、而文件描述符则用于文件I/O 系统调用中。
FILE 是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,包括用于实际 I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。 FILE 数据结构定义在标准 I/O 库函数头文件 stdio.h 中。

三、标准输入、输出、错误

0 1 2 这三个是文件描述符,只能用于文件 I/O read() write() 等),那么在标准 I/O 中,自然是无法使用文件描述符来对文件进行 I/O 操作的,它们需要围绕 FILE 类型指针来进行,在 stdio.h 头文件中有相应的定义,如下:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
所以,在标准 I/O 中,可以使用 stdin stdout stderr 来表示标准输入、标准输出和标准错误。

四、fopen() fread()   fwrite()

在标准 I/O 中,我们将使用库函数 fopen()打开或创建文件, fopen() 函数原型如下所示:
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
函数参数和返回值含义如下:
path 参数 path 指向文件路径,可以是绝对路径、也可以是相对路径。
mode 参数 mode 指定了对该文件的读写权限,是一个字符串,稍后介绍。
返回值: 调用成功返回一个指向 FILE 类型对象的指针( FILE * ),该指针与打开或创建的文件相关联, 后续的标准 I/O 操作将围绕 FILE 指针进行。如果失败则返回 NULL ,并设置 errno 以指示错误原因。

五、fseek 定位

库函数 fseek() 的作用类似于 系统调用 lseek() ,用于设置文件读写位置偏移量, lseek() 用于文件 I/O ,而库函数 fseek() 则用于标准 I/O。

六、检查或复位标志

调用 fread() 读取数据时,如果返回值小于参数 nmemb 所指定的值,表示发生了错误或者已经到了文件末尾(文件结束 end-of-file ),但 fread() 无法具体确定是哪一种情况;在这种情况下,可以通过判断错误标志或 end-of-file 标志来确定具体的情况。
feof() 函数
ferror() 函数
clearerr() 函数

七、格式化IO

在前面编写的测试代码中,会经常使用到库函数 printf() 用于输出程序中的打印信息, printf() 函数可将格式化数据写入到标准输出,所以通常称为格式化输出。除了 printf() 之外,格式化输出还包括: fprintf() 、 dprintf()、 sprintf() snprintf() 4 个库函数。 除了格式化输出之外,自然也有格式化输入,从标准输入中获取格式化数据,格式化输入包括:scanf() 、 fscanf()、 sscanf() 这三个库函数。
 
格式化输出:
C 库函数提供了 5 个格式化输出函数,包括: printf() fprintf() dprintf() sprintf() snprintf() ,其函数定义如下所示:
#include <stdio.h>
int printf(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, ...);
可以看到,这 5 个函数都是可变参函数,它们都有一个共同的参数 format ,这是一个字符串,称为格式控制字符串,用于指定后续的参数如何进行格式转换,所以才把这些函数称为格式化输出,因为它们可以以调用者指定的格式进行转换输出。

格式控制字符串 format​​​​​​​

把这个参数称为格式控制字符串,顾名思义,首先它是一个字符串的形式,其次它能够控制后续变参的格式转换。
格式控制字符串由两部分组成:普通字符(非 % 字符)和转换说明。普通字符会进行原样输出,每个转换说明都会对应后续的一个参数,通常有几个转换说明就需要提供几个参数(除固定参数之外的参数),使 之一一对应,用于控制对应的参数如何进行转换。
转换说明的描述信息需要和与之相对应的参数对应的数据类型要进行匹配,如果不匹配通常会编译报错或者警告! ​​​​​​​
格式化输入:
C 库函数提供了 3 个格式化输入函数,包括: scanf() fscanf() sscanf() ,其函数定义如下所示:
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

八、I/O缓冲

出于速度和效率的考虑,系统 I/O 调用(即文件 I/O open read write 等)和标准 C 语言库 I/O 函数 (即标准 I/O 函数)在操作磁盘文件时会对数据进行缓冲。

1、文件 I/O 的内核缓冲

read() write() 系统调用在进行文件读写操作的时候并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区(kernel buffer cache )之间复制数据。
调用 write()后仅仅只是将 字节数据拷贝到了内核空间的缓冲区中,拷贝完成之后函数就返回了, 在后面的某个时刻,内核会将其缓冲区中的数据写入(刷新)到磁盘设备中,所以由此可知,系统调用 write() 与磁盘操作并不是同步的,write()函数并不会等待数据真正写入到磁盘之后再返回。此外当调用 close() 或显式同步操作(如 fsync()fdatasync()sync())时,操作系统也会将相应文件的缓冲区数据写入磁盘。
如果在此期间,其它进 程调用 read() 函数读取该文件的这几个字节数据,那么内核将自动从缓冲区中读取这几个字节数据返回给应用程序。
我们把这个内核缓冲区就称为文件 I/O 的内核缓冲。这样的设计,目的是为了提高文件 I/O 的速度和效 率,使得系统调用 read()、write()的操作更为快速,不需要等待磁盘操作(将数据写入到磁盘或从磁盘读取 出数据),磁盘操作通常是比较缓慢的。同时这一设计也更为高效,减少了内核操作磁盘的次数。

刷新文件 I/O 的内核缓冲区

强制将文件 I/O 内核缓冲区中缓存的数据写入(刷新)到磁盘设备中,对于某些应用程序来说,可能是 很有必要的,例如,应用程序在进行某操作之前,必须要确保前面步骤调用 write() 写入到文件的数据已经真 正写入到了磁盘中,诸如一些数据库的日志进程。
联系到一个实际的使用场景,当我们在 Ubuntu 系统下拷贝文件到 U 盘时,文件拷贝完成之后,通常在拔掉 U 盘之前,需要执行 sync 命令进行同步操作,这个同步操作其实就是将文件 I/O 内核缓冲区中的数据更新到 U 盘硬件设备,所以如果在没有执行 sync 命令时拔掉 U 盘,很可能就会导致拷贝到 U 盘中的文件遭到破坏!
㈠、 fsync() 函数
㈡、 fdatasync() 函数
㈢、 sync() 函数
对性能的影响
在程序中频繁调用 fsync() fdatasync() sync() (或者调用 open 时指定 O_DSYNC O_SYNC 标志) 对性能的影响极大,大部分的应用程序是没有这种需求的,所以在大部分应用程序当中基本不会使用到。

直接 I/O:绕过内核缓冲

Linux 内核 2.4 版本开始, Linux 允许应用程序在执行文件 I/O 操作时绕过内核缓冲区,从用户空间 直接将数据传递到文件或磁盘设备,把这种操作也称为直接 I/O direct I/O )或裸 I/O raw I/O)。直接 I/O 只在一些特定的需求场合,譬如磁盘速率测试工具、数据库系统等。

​​​​​​​​​​​​​​

2、标准IO的stdio 缓冲

标准 I/Ofopenfreadfwritefclosefseek 等)是 C 语言标准库函数,而文件 I/Oopenreadwrite、 close、lseek 等)是系统调用,虽然标准 I/O 是在文件 I/O 基础上进行封装而实现(譬如 fopen 内部实际上调 用了 openfread 内部调用了 read 等),但在效率、性能上标准 I/O 要优于文件 I/O,其原因在于标准 I/O 实 现维护了自己的缓冲区,我们把这个缓冲区称为 stdio 缓冲区。

通过这样的优化操作,当操作磁盘文件时,在用户空间缓存大块数据以减少调用系统调用的次数,使得 效率、性能得到优化。使用标准 I/O 可以使编程者免于自行处理对数据的缓冲,无论是调用 write() 写入数据、还是调用 read() 读取数据。
stdio 缓冲进行设置
C 语言提供了一些库函数可用于对标准 I/O stdio 缓冲区进行相关的一些设置,包括 setbuf() setbuffer() 以及 setvbuf()

刷新 stdio 缓冲区

㈠、关闭文件时刷新 stdio 缓冲区

 fclose(stdout); //关闭标准输出
㈡、程序退出时刷新 stdio 缓冲区

总结:

从图中自上而下,
1)首先应用程序调用标准 I/O 库函数将用户数据写入到 stdio 缓冲区中, stdio 缓冲区是 由 stdio 库所维护的用户空间缓冲区。
2)针对不同的缓冲模式,当满足条件时, stdio 库会调用文件 I/O (系统 调用 I/O )将 stdio 缓冲区中缓存的数据写入到内核缓冲区中,内核缓冲区位于内核空间。
3)最终由内核向磁 盘设备发起读写操作,将内核缓冲区中的数据写入到磁盘(或者从磁盘设备读取数据到内核缓冲区)。
应用程序调用库函数可以对 stdio 缓冲区进行相应的设置,设置缓冲区缓冲模式、缓冲区大小以及由调 用者指定一块空间作为 stdio 缓冲区,并且可以强制调用 fflush() 函数刷新缓冲区;而对于内核缓冲区来说, 应用程序可以调用相关系统调用对内核缓冲区进行控制,譬如调用 fsync() fdatasync() sync() 来刷新内核 缓冲区(或通过 open 指定 O_SYNC O_DSYNC 标志),或者使用直接 I/O 绕过内核缓冲区( open 函数 指定 O_DIRECT 标志)。

九、文件描述符与 FILE 指针互转

在应用程序中,在同一个文件上执行 I/O 操作时,还可以将文件 I/O (系统调用 I/O )与标准 I/O 混合使 用,这个时候我们就需要将文件描述符和 FILE 指针对象之间进行转换,此时可以借助于库函数 fdopen() 、 fileno()来完成。
库函数 fileno() 可以将标准 I/O 中使用的 FILE 指针转换为文件 I/O 中所使用的文件描述符,而 fdopen() 则进行着相反的操作。
当混合使用文件 I/O 和标准 I/O 时,需要特别注意缓冲的问题,文件 I/O 会直接将数据写入到内核缓冲 区进行高速缓存,而标准 I/O 则会将数据写入到 stdio 缓冲区,之后再调用 write() stdio 缓冲区中的数据写入到内核缓冲区。譬如下面这段代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
printf("print");
write(STDOUT_FILENO, "write\n", 6);
exit(0);
}
执行结果你会发现,先输出了 "write" 字符串信息,接着再输出了 "print" 字符串信息,产生这个问题的原因很简单:
printf函数是标准IO函数,且是行缓冲的,他会将数据先输入到stdio缓冲区中,遇到换行符、程序退出或者主动刷新时才会将数据刷新进内核缓冲区,在终端显示。
write函数是系统调用函数,会直接将数据写入内核缓冲区中。

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

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

相关文章

[office] excel工作表数据分级显示 #其他#笔记

excel工作表数据分级显示 如下图1所示的工作表数据&#xff0c;我们按东区、西区、南区、北区来建立分级显示。 图1 这里先利用“创建组”命令建立分级显示。选取单元格区域A3:E5&#xff0c;单击功能区“数据”选项卡“分级显示”组中的“创建组——创建组…”命令&#xff…

使用Cython编译Python源码加密加速,有这一篇就够了!

0 前言 python是一门脚本语言&#xff0c;运行时由python虚拟机解释执行。当我们使用python设计好算法给第三方使用时只能提供源码&#xff0c;任何运行我们算法的人都可以看到源码以及对应的算法思路。因此&#xff0c;需要一定手动保护源码。 最简单的保护方式是使用代码混…

对待谷歌百度等搜索引擎的正确方式

对待百度、谷歌等搜索引擎的方式是&#xff0c;你要站在搜索引擎之上&#xff0c;保持自己的独立思想和意见。 当谷歌宣布他们将会根据一个名为“Alphabet”的新控股公司来进行业务调整时&#xff0c;在科技界引起了一片恐慌之声。 永远不要说这是一个公司一直在做的事情。不…

【Linux】深入解析动静态库:原理、制作、使用与动态链接机制

文章目录 前言&#xff1a;1. 什么是动静态库2. 动静态库的制作和使用3. 动态库的查找问题4. 理解动态库的加载4.1. 站在系统的角度理解4.2. 编址、可执行程序4.3. 动态库动态链接和加载问题 总结&#xff1a; 前言&#xff1a; 在软件开发中&#xff0c;动静态库是两种重要的…

【栈】1106. 解析布尔表达式

本文涉及知识点 栈 LeetCode 1106. 解析布尔表达式 布尔表达式 是计算结果不是 true 就是 false 的表达式。有效的表达式需遵循以下约定&#xff1a; ‘t’&#xff0c;运算结果为 true ‘f’&#xff0c;运算结果为 false ‘!(subExpr)’&#xff0c;运算过程为对内部表达式…

2024年【R2移动式压力容器充装】考试技巧及R2移动式压力容器充装复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 R2移动式压力容器充装考试技巧参考答案及R2移动式压力容器充装考试试题解析是安全生产模拟考试一点通题库老师及R2移动式压力容器充装操作证已考过的学员汇总&#xff0c;相对有效帮助R2移动式压力容器充装复审考试学…

ubuntu系统 kubeadm方式搭建k8s集群

服务器环境与要求&#xff1a; 三台服务器 k8s-master01 192.168.26.130 操作系统&#xff1a; Ubuntu20.04 k8s-woker01 192.168.26.140 操作系统&#xff1a; Ubuntu20.04 k8s-woker02 192.168.26.150 操作系统&#xff1a; Ubuntu20.04 最低配置&#xff1a;2…

【启程Golang之旅】协程和管道操作

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

【ARM Cache 与 MMU 系列文章 7.6 -- ARMv8 MMU 配置 寄存器使用介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 MMU 转换控制寄存器 TCR_ELxTCR_ELx 概览TCR_ELx 寄存器字段详解TCR 使用示例Normal MemoryCacheableShareability MMU 内存属性寄存器 MAIR_ELxMAIR_ELx 寄存器结构内存属性字段Devic…

人工智能在【肿瘤生物标志物】领域的最新研究进展|顶刊速递·24-06-08

小罗碎碎念 本期文献速递的主题是——人工智能在“肿瘤生物标志物”领域的最新研究进展。 重点关注 今天推荐的6篇文献中&#xff0c;第二篇和第三篇是小罗最喜欢的&#xff0c;因为对于临床来说&#xff0c;比较具有实际意义&#xff0c;也和自己的想法很契合。 尤其是第三篇…

华安保险:核心系统分布式升级,提升保费规模处理能力2-3倍 | OceanBase企业案例

在3月20日的2024 OceanBase数据库城市行的活动中&#xff0c;安保险信息科技部总经理王在平发表了以“保险行业核心业务系统分布式架构实践”为主题的演讲。本文为该演讲的精彩回顾。 早在2019年&#xff0c;华安保险便开始与OceanBase接触&#xff0c;并着手进行数据库的升级…

uniapp自定义的下面导航

uniapp自定义的下面导航 看看效果图片吧 文章目录 uniapp自定义的下面导航 看看效果图片吧 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6aa0e964741d4dd3a58f4e86c4bf3247.png) 前言一、写组件、我这里就没有写组件了直接写了一个页面&#xff1f;总结 前言 在…

ubuntu使用docker安装openwrt

系统&#xff1a;ubuntu24.04 架构&#xff1a;x86 1. 安装docker 1.1 离线安装 docker下载地址 根据系统版本&#xff0c;依次下载最新的三个关于docker的软件包 container.io&#xff08;注意后缀版本顺序&#xff09;docker-ce-clidocker-ce 然后再ubuntu系统中依次按顺…

javaweb学习(day14-ThreadLocal文件上传下载)

一、线程数据共享和安全 -ThreadLocal 1 什么是 ThreadLocal ThreadLocal 的作用&#xff0c;可以实现在同一个线程数据共享, 从而解决多线程数据安全问题. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!] ThreadLocal 可以像 Map 一样存取数…

量化交易:Miniqmt获取可转债数据和交易python代码

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 低风险资产除了国债外&#xff0c;还有可转债&#xff0c;兼容有高收益的股性和低风险的债性&#xff0c;号称“下有保底&#xff0c;上不封顶”。 &#x1f50d; 可转债&#xff1a;金融市场的双面娇娃 可转债&am…

web学习笔记(六十五)

目录 1. Hash模式和History模式 2. 导航守卫 3. 路由元信息 4.路由懒加载 1. Hash模式和History模式 Hash模式&#xff08;哈希模式&#xff09;和History模式&#xff08;历史模式&#xff09;是匹配路由的两种模式&#xff0c;一般默认配置Hash模式&#xff0c;可以在in…

软件架构x86 、 x86_64、 arm64、aarch64

看系统信息: 大多数Linux发行版都提供如 uname -a命令 arch命令用于显示当前主机的硬件架构类型。 例如 下面的是Kylin Linux Advanced Server for Kunpeng V10 操作系统 (鲲鹏处理器是华为在2019年1月向业界发布的高性能数据中心处理器 ) 下面这个是 ubuntu 18.04.6 …

【AI论文与新生技术】Follow-Your-Emoji:精细可控且富有表现力的自由式人像动画技术

我们提出了 Follow-Your-Emoji&#xff0c;这是一种基于扩散的肖像动画框架&#xff0c;它使用目标地标序列对参考肖像进行动画处理。肖像动画的主要挑战是保留参考肖像的身份并将目标表情转移到该肖像&#xff0c;同时保持时间一致性和保真度。为了应对这些挑战&#xff0c;Fo…

四、利用启发式算法进行特定数据集的残差网络结构搜索【框架+源码】

背景&#xff1a;工作之后干的事情跟算法关联甚少&#xff0c;整理下读书期间的负责和参与的work&#xff0c;再熟悉学习下。 边熟悉边整理喽~ CV Tradictional workCV AI based work机械臂视觉抓取项目机器学习全流程 Pipeline训练平台OCR生产线喷码识别三维重建(SfM)ROS机器人…

SpringBoot Elasticsearch06-以黑马商场为例-黑马程序员学习笔记

黑马商城作为一个电商项目&#xff0c;商品的搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 首先&#xff0c;查询效率较低。 由于数据库模糊查询不走索引&#xff0c;在数据量较大的时候&#xff0c;查询性能很…