IO标准IO文件IO

发布时间:2026/7/23 10:05:51
IO标准IO文件IO IOIO概述IOInput/Output是计算机系统中数据在不同实体间传输交互的抽象过程涵盖了与外部设备如文件、硬件、网络的数据读写行为。本质上它是程序与外部环境建立“数据通道”的核心机制支撑着程序获取输入、输出结果的基本逻辑是软件与硬件、本地与远程交互的底层依托IO作用IO是程序与外部环境交互的基础桥梁。一方面它为程序提供“输入”能力支撑程序从文件、硬件、网络获取运行所需的数据如游戏加载资源文件、程序读取用户输入另一方面通过“输出”功能程序可能将计算结果持久化存储如写入日志文件、可视化展示如输出到终端或远程传输如网络数据发送IO种类按现实层级分类系统调用IO也称之为文件IO直接对接操作系统内核的最底层接口逻辑极简、性能接近硬件但是需要手动处理缓存区、错误码等细节库函数IO也称之为标准IO基于系统调用封装的高层接口自动管理缓存区、跨平台兼容、简化开发是用户态程序的常用选择按应用场景分类文件 IO针对磁盘文件的读写如日志记录、配置文件操作。可通过“系统调用”或“库函数”实现设备IO与硬件设备交互如键盘输入、显示器输入。操作系统通过“设备抽象”将设备转换为文件系统中的特殊文件使程序能用统一的文件操作接口访问所有硬件这正是Linux“一切皆文件”理念的核心网络IO实现网络数据收发如浏览器访问页面、APP通讯。依赖系统调用或者是更高层的框架系统调用与库函数的区别对比维度系统调用库函数层级定位操作系统内核提供的底层接口如open基于系统调用/纯用户逻辑封装的高层工具如C标准库fopen运行空间运行于内核空间需切换CPU特权级用户态→内核态运行于用户空间无需特权级切换功能本质直接操作硬件/内核资源如读写磁盘、创建进程1.封装系统调用实现复杂功能如printf调用write 2.纯用户态逻辑如strlen计算字符长度)性能开销因“用户态→内核态”切换开销较大1.若封装系统调用开销略低减少切换次数 2.纯用户态函数开销极小移植性依赖操作系统内核 移植性差如Linux与Windows系统调用不同跨平台苦逼如C标准库移植性好同一函数适配多系统标准IO介绍定义标准I/O是C库提供的输入输出函数集合由ANSI C 标准统一规范为跨平台IO操作奠定基础跨平台性在UNIX系统、Windows系统、Linux 等多系统操作均实现标准I/O库让代码跨平台开发更顺畅高效实现原理通过缓冲机制内存临时存储数据减少直接系统调用次数降低硬件交互开销提高IO效率易用性设计基于系统底层调用封装屏蔽复杂硬件操作开发者无需关注设备细节简单调用函数即可完成IO流stream定义所有I/O操作抽象为字节的“移入/移出”程序的过程这种字节流动形态称为流。分类文件流按字符编码ASCII、UTF-8处理注意文本语义二进制流直接操作原始字节保留数据原始形态如图片、可执行文件默认打开的流1stdin标准输入流如键盘输入2stdout标准输出流如屏幕打印3stderr标准错误流专用于输出错误信息FILE对象定义FILE对象是一个结构体用于存储文件或流相关的所有状态信息如文件描述符、缓冲区大小、缓冲区当前位置、文件结束标志符等等FILE对象与流对应关系每个流在内存中由一个FILE对象表示通过FILE*指针访问标准流的特殊FILE对象1stdin是标准输入流对应的FILE对象指针通常关联到键盘输入2stdout是标准输出流对应的FILE对象指针通常关联到控制台输出3stderr是标准错误流对应的FILE对象指针。通常关联到控制台用于输出错误信息FILE对象由于FILE对象通常较大且内部结构复杂标准I/O函数不直接操作FILE对象本身而是通过FILE指针FILE*来间接访问。FILE指针是指向FILE对象的内存地址所有文件操作如打开、读写、关闭都基于这个指针进行FILE指针创建与销毁通过fopen()进行创建FILE指针通过fclose()进行销毁FILE指针FILE指针操作所有标准I/O函数都以FILE指针作为参数常见操作包括读写操作、定位操作、错误检查等等FILE对象与底层系统调用的关系FILE对象是标准库层面的抽象而Linux内核通过文件描述符如0、1、2对应标准输入/输出/错误管理文件。FILE对象内部维护了一个文件描述符吧将标准I/O函数映射到底层系统调用如read()、write()并提供缓冲区优化标准IO的缓冲区定义标准I/O中的缓冲区是C语言标准库为 优化文件读写效率设计 的内存机制本质上就是一段由FILE结构体管理的内存空间作用通过临时存储数据、批量处理可以减少硬件IO如磁盘、终端的交互次数提示程序效率缓冲策略 标准I/O中设计了行缓冲、全缓冲、无缓冲三种策略来适配不同的场景也支持手动修改行缓冲默认用于终端设备如stdout、stdinLinux系统中行缓冲的大小为10241KB全缓冲默认用于文件如.txtLinux系统中行缓冲的大小为40964KB无缓冲不使用临时内存暂存数据直接 写入硬件如stder无缓冲区的大小为0行缓冲刷新时机1遇到换行符n会刷新缓冲区2程序正常结束会刷新缓冲区3当文件关闭时会刷新缓冲区4当输入和输出发生切换时会刷新缓冲区5缓冲区写满未到换行符会刷新缓冲区6主动调用fflush函数会强制刷新缓冲区fflush函数所需头文件#includestdio.h原型intfflush(FILE*stream);功能将指定流stream缓冲区中尚未写入的内容强制写入到对应的输出设备如终端、文件等中 参数stream用于指定需要刷新缓冲区的流 返回值成功返回0刷新了流缓冲区 失败返回EOF并将error设置为具体的错误类型全缓冲刷新时机1程序正常结束会刷新缓冲区2当文件关闭时会刷新缓冲区3当输入和输出发生切换时会刷新缓冲区4缓冲区写满未到换行符会刷新缓冲区5主动调用fflush函数会强制刷新缓冲区手动设置缓冲为什么需要手动设置缓冲1覆盖默认缓冲策略不同系统/语言对IO流的默认缓冲设置不同例如C语言中stdout默认行缓冲stderr默认无缓冲手动设置可按需调整2性能与实时性平衡①行缓冲适合交互式输出如命令行程序每行输出后自动刷新②全缓冲适合大量数据批量写入如日志文件减少IO次数③无缓冲适合需要立即 显示的关键信息如错误日志3特殊场景需求例如网络编程中避免数据堆积 或调试时强制输出中间结果手动设置缓冲区典型应用场景1日志系统根据日志级别设置缓冲错误日志无缓冲普通日志全缓冲2交互式工具命令行程序中确保用户输入后立即响应3嵌入式系统受限内存环境下自定义缓冲区大小setbuf函数所需头文件#includestdio.h原型voidsetbuf(FILE*stream,char*buf);功能设置指定文件流的缓冲区 参数stream指向FILE对象的指针标识要设置缓冲区的文件流 buf指向用户提供的缓冲区的指针 如果提供了缓冲区其大小必须至少为BUFSIZ字节通常在stdio.h中定义 如果是NULL则是无缓冲模式 返回值无setbuf函数允许两种方式的缓冲全缓冲;如果buf指向一个大小大于0的缓冲区那么IO操作将使用全缓冲模式无缓冲如果buf为NULL或者是大小为0那么IO操作将会变为无缓冲模式setbuf函数注意事项1调用setbuf函数应该在打开文件流或者是任何IO操作之前否则可能不会生效2如果要使用自定义的缓冲区确保缓冲区足够大以避免缓冲区溢出3当使用自定义缓冲区时不要在buf指向的缓冲区被释放后再进行IO操作以免导致未定义的行为sleep函数所需头文件#includeunistd.h原型unsignedintsleep(unsignedintseconds);功能用于让程序暂停执行指定的时间 参数seconds指定程序暂停执行的秒数 返回值成功返回0失败若程序被信号中断返回未休眠的秒数setvbuf函数所需头文件#includestdio.h原型intsetvbuf(FILE*stream,char*buf,intmode,size_tsize);功能更加灵活地控制文件流的缓冲行为允许指定缓冲模式和缓冲区的大小 该函数必须在对stream执行任何其他操作前调用 参数stream指向FILE对象的指针标识要设置缓冲区的文件流 buf:指向用户提供的缓冲区的指针 如果为NULL函数会自动分配一个大小为size的缓冲区 mode缓冲区模式填写以下宏 _IOFBF fully buffered 全缓存 _IOLBF line buffered 行缓存 _IONBF unbuffered 无缓存 当mode为_IONBF时size参数被忽略 size:缓冲区的大小以字节为单位 返回值成功返回0失败返回非0值 练习如何将行缓存设置为全缓存setvbuf函数注意事项1setvbuf必须在文件流执行任何读写操作之前调用文件流初始化后否则可能导致设置失败或触发未定义行为2若使用自定义缓冲区buf ! NULL需确保缓冲区大小size与实际需求匹配。若size过小可能导致数据溢出若size过大会浪费内存资源3若使用自动分配缓冲区buf NULLsize需设置合理值通常建议不小于系统默认缓冲区大小如BUFSIZ避免因缓冲区过小影响性能或触发溢出4禁止在缓冲区被释放后对文件流执行IO操作否则会触发未定义行为如访问已释放内存导致崩溃5当mode为_IONBF(无缓冲)时size参数会被忽略即使设置非零值也无效需避免依赖size配置无缓冲模式6不同系统对mode的支持可能有差异如某些嵌入式环境可能不支持行缓冲_IOLBF需要根据实际平台验证行为标准IO1.提高程序的可移植性2.减少系统调用次数提高系统效率缓存行缓存1024刷新缓冲区的条件1.回车2.程序结束3.强行刷新fflush4.缓存区满练习刷新行缓存的条件全缓存40961.程序结束2.强制刷新3.缓存区满无缓存标准错误输出是无缓存练习计算行缓存的大小利用结构体man手册使用方式man手册是用于在linux系统中查询命令、函数等的帮助手册不同的章节分类便于快速定位获取各类linux系统相关的帮助信息以下是一些常用用法man man查看man手册自身的章节说明了解man手册整体架构和使用规范man 1 要查看的内容查看可执行程序或shell命令的帮助文档“1”对应用户命令章节man 2 要查看的内容查看系统调用内核提供的函数相关帮助“2”对应系统调节章节man 3 要查看的内容查看库调用库中的函数“3”对应库函数章节内核中错误码的问题在Linux系统中内核通过预定义宏如ENOENT标识错误错误码为正整数内核返回时用负数传递错误码转换为错误信息-strerror函数所需头文件#includestring.h原型char*strerror(interrnum);功能将错误码转换为错误信息 参数errnum错误码 返回值成功返回指向错误信息的字符串指针 若errnum无效返回结果由系统实现决定通常返回通用错误描述如Unknown error nnn错误信息打印函数perror函数所需头文件#includestdio.h原型voidperror(constchar*s);功能打印错误信息 如果s不为NULL且*s不为空字节(\0)则打印参数字符串s后跟一个冒号和一个空格 然后打印与当前error值对应的错误消息和一个换行符 参数s用户的附加信息 若s非空(s!NULL*s!\0)则输出s:字符串冒号空格 若s为空(sNULL||*s\0)则不同系统可能输出错误信息或直接输出错误信息返回值无对文件操作的步骤1、打开文件所需头文件#includestdio.h函数原型FILE*fopen(constchar*pathname,constchar*mode);功能用标准IO的方式打开文件 返回值成功返回FILE指针失败返回NULL重置错误码 参数 pathname文件路径和名字不写路径默认打开当前路径下的文件 mode打开文件的方式 r以只读的方式打开文件将光标定位到文件开头如果文件不存在则会报错 r以读写的方式打开文件将光标定位到文件开头如果文件不存在则会报错 w以只写方式打开文件如果文件存在就清空如果文件不存在就创建将光标定位到开头 w以读写方式打开文件如果文件存在则清空如果文件不存在则创建将光标定位到结尾 a以追加的方式打开文件如果文件不存在则创建如果文件存在不会清空将光标定位到结尾 a以读和追加的方式打开文件如果文件不存在就创建如果文件存在不会清空 如果读操作光标在文件开头如果写操作总是在文件结尾 练习1、使用fgetc完成对文件的读取2、通过不同的权限创建文件3、模拟cp命令的实现2、读文件内容1、fgetc函数 所需头文件#includestdio.h原型intfgetc(FILE*stream);功能从文件流中读取当前位置的字符 每次调用fgetc会自动移动文件指针指向下一个字符位置为后续读取做准备 参数stream文件指针 返回值成功返回无符号字符(unsignedchar)的数值(范围0~255)并转换为int返回 若返回char255(unsignedchar)会被视为-1(signedchar),与EOF冲突 失败或读到文件末尾返回EOF-12、fgets函数 所需头文件#includestdio.h原型char*fgets(char*s,intsize,FILE*stream);功能在文件stream中读取字符串将读取的内容存储到由s指向的缓冲区中 最多读取size-1个字符含可能的换行符\n读取完成后会自动在缓冲区末尾补充字符串结束符\0fgets遇到文件结束符EOF或换行符\n时停止换行符也会被一同存储到s中 参数s用于存放读到的内容的内存首地址 size想要读取的字节数 stream文件指针 返回值读取成功返回字符串s读取失败或者是读取到文件结束返回NULL3、fread函数 所需头文件#includestdio.h原型size_tfread(void*ptr,size_tsize,size_tnmemb,FILE*stream);功能从stream中读取nmemb项数据每项大小为size将它们存入ptr指向的位置中 参数 ptr存储读到的数据首地址 size每个数据项的大小单位是字节 一般将其置为1让项目数表示字节数 nmemb要读取的数据项的最大数量 stream文件指针 返回值成功返回实际读取的数据项数量 返回值小于nmemb时可能是遇到了文件结束EOF或者是出现了错误 返回值为0就是没有读取到任何的数据 fread不区分文件结束EOF或者是读取错误调用者必须使用feof函数或者ferror函数来区分场景3、写入1、fputc函数 所需头文件#includestdio.h原型intfputc(intc,FILE*stream);功能向文件中写入一个字符 参数c要写入的字符 stream文件指针 返回值成功返回写入的字符的ASCII码失败返回EOF2、fputs函数 所需头文件#includestdio.h原型intfputs(constchar*s,FILE*stream);功能将字符串s的内容写到stream指向的文件中仅写入字符串有效字符不包含末尾\0 参数s要写入字符串的首地址 stream文件指针 返回值成功返回非负整数失败返回EOF-13、fwrite函数 所需头文件#includestdio.h原型size_tfwrite(constvoid*ptr,size_tsize,size_tnmemb,FILE*stream);功能按类型进行数据写入 参数 ptr指向要写入数据的内存首地址 size每个数据项的大小单位是字节 一般将其置为1让项目数表示字节数 nmemb要写入的数据项的最大数量 stream文件指针 返回值成功返回实际写入的数据项数量 返回值小于nmemb时可能是写入错误或到达文件末尾 使用按类型写入的方式实现cp命令4、关闭文件‍所需头文件#includestdio.h原型intfclose(FILE*stream;功能用标准IO的方式关闭文件 参数stream:文件指针 返回值成功返回0失败返回EOF(-1),重置错误码5.文件流指针的偏移1.fseek函数 所需头文件 原型intfseek(FILE*stream,longoffset,intwhence);函数功能设置光标的位置 参数stream文件指针 offset光标的偏移0向后偏移0不偏移0向前偏移 whencs从哪个位置偏移SEEK_SET从开头开始偏移SEEK_END从当前位置偏移SEEK_CUR从结尾的位置偏移 返回值成功返回0失败返回-1并设置为相应的错误码 练习实现对图片的加密操作2.rewind函数 所需头文件#includestdio.h原型voidrewind(FILE*stream);功能将光标恢复到文件的开头 参数stream:文件指针 返回值无3.ftell函数 所需头文件#includestdio.h原型longftell(FILE*stream);功能返回文件流当前位置相对于文件起始位置的偏移量(以字节为单位)参数stream:文件指针 返回值成功返回字节数 失败返回-1并设置为相应的错误码文件 IO定义文件IO是在POSIX可移植操作系统接口中定义的一组输入输出的函数涉及操作系统和应用程序之间的交互用于读取和写入存储在永久性存储介质如硬盘、SSD、USB闪存盘等上的数据跨平台性不同的操作系统如UNIX、Windows、Linux的文件IO机制存在差异接口、调用方式和底层实现逻辑各有特点如Windows下有CreateFile等专属函数Linux则基于open、read等系统调用高效实现原理1.直接交互硬件/文件系统绕过高级缓冲直接发起系统调用2.无用户态缓冲不依赖用户态缓冲机制每次读写更贴近硬件真实操作但频繁调用会增加系统开销需开发者按需优化如手动合并读写请求易用性设计1.贴近底层逻辑需开发者熟悉操作系统API、文件权限、设备特性等直接处理缓冲区管理、错误码解析Linux中errno对开发经验要求高2.灵活但复杂可精准控制文件打开模式只读、读写等、数据传输细节却也因需手动处理资源释放如close函数、并发冲突等问题增加开发复杂度特点1.没有缓冲机制每次调用都会引起系统调用2.围绕文件描述符进行操作相当于标准IO中的流FILE*文件描述符是正数3.默认打开三个描述符标准输入(0) 标准输出(1) 错误输出(2)4.除目录文件其他类型文件都可以操作5.可移植性相对较弱对比维度文件IO标准IO概念对文件系统中的文件进行读写程序与终端控制台交互用途持续化存储、跨程序数据交换即时交互、简单调试操作对象磁盘文件(需路径或描述符)以流为抽象覆盖终端和磁盘文件数据流向程序⇄磁盘持久化程序⇄终端临时交互缓冲机制默认缓冲stdout行缓冲 stderr无缓冲错误处理需显式处理文件不存在、权限不足等通常无需特殊处理终端直接反馈典型场景配置文件读写、日志记录、数据存储等命令行参数输入、结果显示、错误提示等等性能特点大文件或批量读写文件IO通过手动优化调整读写块大小频繁小数据读写时标准IO的缓冲机制更高效生命周期数据持久保存会话结束后数据丢失文件描述符定义文件描述符 是操作系统内核为每个打开的文件/设备分配的唯一整数标识符用于标识和跟踪进程与文件的交互在Linux/Unix中它是底层文件IO操作的核心参数本质1.它是指向内核中维护的文件表File Table该表中记录了文件的状态位置指针等信息2.每个进程都有自己独立的文件描述符表通常是从0开始编号相互不干扰Linux系统中一个正在执行的程序中文件描述符的范围是[0-1023]可通过ulimit调整一般使用默认值 作用1.连接用户进程与内核资源通过文件描述符进程可以请求内核进行文件读写、设备操作等无需关心底层实现细节2.资源管理操作系统通过文件描述符跟踪哪些文件被哪些进程打开并在进程关闭文件或退出时自动回收资源特点1.唯一性同一个进程内每个文件描述符对应唯一的打开文件2.非负数整数通常是小整数如0、1、2但可动态分配如open()返回的新描述符3.跨函数传递可在不同系统调用间传递代表对同一文件的持续操作常见文件描述符编号编号宏定义用途对应设备0STDIN_FILENO标准输入Standard Input通常是键盘1STDOUT_FILENO标准输出Standard Output通常是终端屏幕2STDERR_FILENO标准错误Standard Error通常是终端屏幕打开文件时会默认打开三个文件描述符分别是输入 0输出 1错误输出 2特点适用于Linux或Unix操作系统在用户空间无缓存用途用于驱动开发无标准库提供的场景对文件的操作1、打开文件所需头文件#includesys/types.h#includesys/stat.h#includefcntl.h原型intopen(constchar*pathname,intflags);intopen(constchar*pathname,intflags,mode_tmode);功能用文件IO的方式打开文件 参数pathname文件路径和名字 不写路径默认打开当前路径下的文件 flags打开文件的方式 O_CREAT:文件不存在则创建文件 如果flags中填写了O_CREAT就必须填写第三个参数第三个参数代表创建文件的权限 O_EXCL只能与O_CREAT连用如果文件不存在则创建如果文件存在则报错错误码为EEXIST O_RDONLY:以只读方式打开文件文件不存在则打开失败 O_WRONLY:以只写方式打开文件文件不存在则打开失败 O_TRUNC:如果文件存在就清空 O_APPEND:以追加方式打开文件文件不存在则打开失败 O_RDWR:以读写方式打开文件文件不存在则打开失败 mode创建文件的权限一般用8进制表示 实际创建出来的文件权限(mode~umask)O_CREAT存在时会设置文件创建的权限通常填0666umask文件的掩码通过umask命令可以查看掩码的默认值是0002~umask文件掩码的取反是在最大的文件权限的基础上进行取反最大文件的权限是0666实际创建出来的文件权限(0666~(0002))(06660664)0664rO_RDONLY rO_RDWR wO_WRONLY|O_CREAT|O_TRUNC wO_CREAT|O_TRUNC|O_RDWR a:O_APPEND|O_CREAT|O_RDONLY a:O_APPEND|O_CREAT|O_RDWR 返回值成功返回文件描述符失败返回-1错误码errno会被正确设置标准IO和文件IO打开方式对比文件IO标准IO功能O_RDONLY“r”以只读的方式打开文件将光标定位到文件开头O_RDWR“r”以读写的方式打开文件将光标定位到文件开头O_WRONLY | O_CREAT | O_TRUNC,0666“w”以只写方式打开文件如果文件存在就清空如果文件不存在就创建将光标定位到开头O_CREAT | O_TRUNC | O_RDWR,0666“w”以读写方式打开文件如果文件存在则清空如果文件不存在则创建将光标定位到结尾O_APPEND | O_CREAT | O_RDONLY,0666“a”以追加的方式打开文件如果文件不存在则创建如果文件存在不会清空将光标定位到结尾O_APPEND | O_CREAT | O_RDWR,0666“a”以读和追加的方式打开文件如果文件不存在就创建 如果文件存在读光标始终在开头写光标始终在结尾2、对文件进行读写1.read函数 所需头文件#includeunistd.h原型ssize_tread(intfd,void*buf,size_tcount);功能用文件IO的方式读取文件中的内容 参数fd文件描述符 buf存储读取到的数据的首地址 count期望读取的字节数 返回值成功返回读取到的字节数读到文件末尾返回0失败返回-1错误码errno会被正确设置2.write函数 所需头文件#includeunistd.h原型ssize_twrite(intfd,constvoid*buf,size_tcount);功能用文件IO的方式往文件中写入内容 参数fd文件描述符 buf想要写入数据的首地址 count期望写入的字节数 返回值成功返回写入的字节数返回0表示不会写任何的数据失败返回-1错误码errno会被正确设置 练习1、查询man手册找到上述两个函数的参数返回值2、使用文件io实现cp命令3、偏移所需头文件#includesys/types.h#includeunistd.h原型off_tlseek(intfd,off_toffset,intwhence);功能定位光标的位置光标实际指文件偏移量 参数fd:文件描述符 offset偏移量向左负数 向右正数 whencs:SEEK_SET放在文件头SEEK_END放在文件尾SEEK_CUR当前指针位置 返回值 练习使用文件io实现对图片的加密取前十个字节数据进行逆序4、关闭文件所需头文件#includeunistd.h原型intclose(intfd);功能用文件IO的方式关闭文件 参数fd文件描述符 返回值成功返回0失败返回-1错误码errno会被正确设置目录操作1、打开目录 DIR*opendir(constchar*name);功能打开目录 返回值目录流指针 参数目录名 通常使用命令行参数argv[]2、读目录的内容structdirent*readdir(DIR*dirp);功能读取目录内容 返回值 成功返回目录下一个文件的structdirent结构首地址读到结尾返回NULL失败返回NULL并置errno 参数dirp目录流指针structdirent*类型的结构体内容structdirent{ino_td_ino;off_td_off;unsignedshortd_reclen;unsignedchard_type;chard_name[256];};3、关闭目录intclosedir(DIR*dirp);功能关闭目录 返回值成功返回0失败返回-1参数dirp目录流指针 练习 模拟ls命令的实现