当前位置: 首页 > news >正文

嵌入式 - Linux软件编程:标准IO

目录

一、标准 IO 基础概念

二、标准 IO 核心接口与操作流程

1. 打开与关闭文件

fopen 函数:用于打开文件并建立流,原型为FILE *fopen(const char *pathname, const char *mode)。

 核心区别对比表

 详细说明与关键差异

重要注意事项

示例

fclose 函数:用于关闭已打开的流,原型为int fclose(FILE *stream)。

2. 读写操作接口

字符级读写:fputc(写字符)和fgetc(读字符)。例如,fputc('A', fp)将字符 'A' 写入流fp,fgetc(fp)从流fp中读取一个字符;

字符串级读写:fputs(写字符串)和fgets(读字符串)。fputs("hello", fp)写入字符串 "hello",fgets(buf, 100, fp)从流中最多读取 99 个字符到buf(预留一个位置存结束符 '\0');

格式化读写:fprintf(格式化输出)和fscanf(格式化输入)。例如,fprintf(fp, "name: %s", name)按格式写入数据,fscanf(fp, "%d", &num)从流中读取整数到num;

3. 定位操作

4. 操作流程示例

三、特殊流与缓存机制

1. 三个默认打开的流

2. 缓存机制

四、man 手册:查询标准 IO 的利器

五、实战

1. 文件拷贝(fgets 与 fputs 实现)

2. 带记忆功能的数字平均值计算


在 Linux 系统编程中,IO 操作是与外部设备交互的核心环节。其中,标准 IO 作为 C 语言库提供的高级 IO 接口,凭借其缓存机制和简洁的接口设计,成为处理文件和设备交互的常用选择。本文将从标准 IO 的基础概念出发,详细解析其核心接口、缓存机制,并结合实践任务帮助理解与应用。

一、标准 IO 基础概念

标准 IO(Standard I/O)是基于 C 语言库实现的 IO 接口,其设计初衷是简化用户对文件的操作,并通过缓存机制提高 IO 效率。与直接操作内核的文件 IO 不同,标准 IO 为开发者提供了更上层的抽象,屏蔽了底层硬件的细节差异。

标准 IO 的操作对象主要是普通文件,而普通文件又可分为两类:

  • ASCII 码文件:所有内容均为可在屏幕上显示的 ASCII 字符,如程序源码、文本文件等,这类文件可直接通过文本编辑器阅读;
  • 二进制文件:内容以数据的二进制形式存储,包含不可显示的字符,如图片、音视频、压缩包等。需要注意的是,ASCII 码文件本质上也是一种特殊的二进制文件。

无论是哪种文件,标准 IO 都通过 “流(stream)” 的概念进行操作 —— 打开文件后,程序与文件之间建立一个流,所有读写操作都通过流来完成。

二、标准 IO 核心接口与操作流程

标准 IO 的操作遵循 “打开 - 读写 - 关闭” 的基本流程,围绕这一流程提供了一系列接口函数。

        标准 IO 的接口:

  1. fopen/fclose
  2. fgetc/fputc
  3. fgets/fputs
  4. fscanf/fprintf
  5. fread/fwrite
  6. fseek/rewind/ftell
  7. 操作步骤:
  • 打开文件
  • 读写文件
  • 关闭文件

1. 打开与关闭文件

  • fopen 函数:用于打开文件并建立流,原型为FILE *fopen(const char *pathname, const char *mode)

原型:FILE *fopen(const char *pathname, const char *mode);

功能:

         打开pathname指向字符串对应的文件,并且和它建立一个流

参数:

         pathname:要打开的文件路径字符串

         mode:打开的方式

         r         只读         文件存在只读打开,文件不存在报错

         r+        读写         文件存在读写打开,文件不存在报错

         w         只写         文件存在清0,文件不存在创建,只写打开

         w+       写读         文件存在清0,文件不存在创建,写读打开

         a         追加         文件存在追加,文件不存在创建,只写打开

         a+       追加读写           文件存在追加,文件不存在创建,写读打开

返回值:

         成功返回FILE*指针

         失败返回NULL

补充:

 C语言 fopen 函数的几种主要模式及其核心区别总结如下:

 核心区别对比表

模式读写权限文件存在时文件不存在时文件指针初始位置主要用途
"r"只读打开打开失败开头安全读取已有文件
"r+"读写打开打开失败开头读取并修改已有文件内容
"w"只写清空内容​(长度归零)创建新文件开头覆盖写入或创建新文件
"w+"读写清空内容​(长度归零)创建新文件开头清空文件后读写(从头操作)
"a"只写保留内容创建新文件末尾​(追加写入)日志追加(防覆盖旧数据)
"a+"读写保留内容创建新文件读-开头 / 写-末尾​ 🌟读取后追加数据(如更新日志)

 详细说明与关键差异

  1. ​**"r"(只读)​**​

    • 最严格​:文件必须存在,否则失败。
    • 指针在开头,适合仅读取场景(如配置文件解析)。
  2. ​**"r+"(读写)​**​

    • 文件必须存在,否则失败。
    • 可修改任意位置​(指针可自由移动),但不会自动清空文件​(需手动覆盖内容)。
  3. ​**"w"(只写)​**​

    • 破坏性最强​:立即清空文件!
    • 适合覆盖写入​(如生成新报告),误用会导致数据丢失⚠️。
  4. ​**"w+"(读写)​**​

    • 同 "w" 会先清空文件,但支持读取(清空后从头读写)。
    • 可用于重建文件结构​(如初始化空数据库文件)。
  5. ​**"a"(追加只写)​**​

    • 最安全​:永不覆盖原有内容,新数据总是写在末尾。
    • 适合日志记录、连续数据采集(如传感器数据保存)。
  6. ​**"a+"(追加读写)​**​ 🌟

    • 唯一分离式指针​:
      • 读取时从开头开始(可读历史数据)。
      • 写入时强制跳到末尾​(避免中间插入破坏数据)。
    • 典型场景:读取日志分析后追加新条目。

重要注意事项

  • ​**"w"/"w+" 的破坏性​:打开即清空,​务必确认文件可覆盖**​!
  • ​**"a"/"a+" 的写入特性​:
    即使使用 fseek() 移动指针,写入时仍会
    强制回到末尾**​(追加是绝对操作)。
  • ​**"r"/"r+" 的必要性**​:若文件可能不存在,需先检查(如 access())或改用 "a"/"w" 系。
  • 二进制文件​:在模式后加 "b"(如 "rb+"),处理非文本数据(如图像)。

示例

// 安全追加日志(文件不存在则创建)
FILE *log = fopen("debug.log", "a+"); 
if (log) {fseek(log, 0, SEEK_SET); // 跳到开头读取历史char buf[256];while (fgets(buf, sizeof(buf), log)) { /* 分析日志 */ }fprintf(log, "[New] Event recorded.\n"); // 自动追加到末尾fclose(log);
}

  • fclose 函数:用于关闭已打开的流,原型为int fclose(FILE *stream)
    • 关闭成功返回 0,失败返回 EOF(-1);
    • 关闭流的同时会刷新缓存,因此操作完成后务必关闭流,避免数据丢失。

原型:int fclose(FILE *stream);

功能:

         关闭已经打开的流

参数:

         stream:文件流指针

返回值:

         成功返回0,失败返回EOF(-1)

2. 读写操作接口

标准 IO 提供了多种读写函数,适用于不同场景:

  1. 字符级读写fputc(写字符)和fgetc(读字符)。例如,fputc('A', fp)将字符 'A' 写入流fpfgetc(fp)从流fp中读取一个字符;
  • fputc

原型:int fputc(int c, FILE *stream);

功能:

         将字符c写入流中

参数:

            c:要写入的字符

           stream:文件流指针

返回值:

         成功返回输出的ASCII码值

           失败返回EOF

注意: putchar()等价于fputc(ch, stdout)

  • fgetc

原型:int fgetc(FILE *stream);

功能:

         读取流中的下一个字符

参数:

         stream:文件流指针

返回值:

         成功返回读到字符的ASCII码值

         失败或者读到文件末尾返回EOF

注意: ch = getchar()等价于 ch = fgetc(stdin)

  • 字符串级读写fputs(写字符串)和fgets(读字符串)。fputs("hello", fp)写入字符串 "hello",fgets(buf, 100, fp)从流中最多读取 99 个字符到buf预留一个位置存结束符 '\0')
  • fputs

原型:int fputs(const char *s, FILE *stream);

功能:

 向流中写入s指向的字符串

参数:

 s:要写入的字符串的首地址

 stream:文件流指针

返回值:

 成功返回非负数

 失败返回EOF

注意: fputs不会多打印\n字符 puts会多打印一个\n字符

  • fgets

原型:char *fgets(char *s, int size, FILE *stream);

功能:

 从流中最多读size-1个字节数据放入s指向的空间中,遇到\n读取截止

参数:

 s:存放数据空间的首地址

 size:最多存放元素的个数

 stream:文件流指针

返回值:

 成功返回存放数据空间的首地址

 失败返回NULL

 读到文件末尾返回NULL

注意: gets会去掉从终端接收的\n字符 fgets不会去掉从终端接收的\n字符

  • 格式化读写fprintf(格式化输出)和fscanf(格式化输入)。例如,fprintf(fp, "name: %s", name)按格式写入数据,fscanf(fp, "%d", &num)从流中读取整数到num
  • fprintf

原型:int fprintf(FILE *stream, const char *format, ...);

功能:

 将格式化的字符串打印在流中

参数:

 stream:文件流指针

 format:格式化的字符串

 ...:参数

返回值:

 成功返回打印字符的个数

 失败返回负数

  • fscanf

原型:int fscanf(FILE *stream, const char *format, ...);

功能:

 从流中接收格式化的字符串

参数:

 stream:文件流指针

 format:格式化字符串

 ...:参数

返回值:

 成功返回匹配的输入控制符的个数

 失败返回EOF

 读到文件末尾返回EOF

  • 块级读写freadfwrite,适用于二进制文件,可一次性读写指定大小的数据块。

3. 定位操作

  • fseek:用于移动文件指针,原型int fseek(FILE *stream, long offset, int whence),可实现文件内的随机访问;
  • ftell:返回当前文件指针位置;
  • rewind:将文件指针重置到文件开头。

4. 操作流程示例

// 打开文件
FILE *fp = fopen("test.txt", "w+");
if (fp == NULL) {perror("fopen error");return -1;
}// 写入数据
fputs("hello standard IO", fp);// 移动指针到开头
rewind(fp);// 读取数据
char buf[100];
fgets(buf, sizeof(buf), fp);
printf("read: %s\n", buf);// 关闭文件
fclose(fp);

三、特殊流与缓存机制

1. 三个默认打开的流

程序启动时,系统会自动打开三个特殊流,无需手动 fopen:

  • stdin:标准输入流,对应终端输入(如键盘);
  • stdout:标准输出流,对应终端输出;
  • stderr:标准出错流,用于输出错误信息。

  • printf、puts、putchar都是通过stdout流实现在终端信息打印

  • scanf、gets、getchar都是通过stdin流实现获得终端信息

  • perror通过stderr流实现在终端打印出错信息

2. 缓存机制

标准 IO 的高效性源于其缓存机制 —— 数据先写入缓存,满足一定条件后再批量写入内核或设备,减少系统调用次数。缓存分为三类:

.标准IO缓存:

  1. 标准IO是一种有缓存的IO
  2. 效率高
  3. 实时性差

  • 全缓存:缓存区满(通常 4k)时刷新,或通过fflush强制刷新、关闭流时刷新,适用于普通文件;
    • 缓存区满刷新缓存

      刷新条件:

              缓存区满4k刷新

              fflush函数强制刷新

              fclose或者程序关闭刷新

      与文件建立的缓存一般为全缓存

  • 行缓存:遇到换行符\n或缓存区满(通常 1k)时刷新,适用于终端(stdinstdout);
    • 遇到\n刷新缓存区

      刷新条件:

              缓存区满1k刷新

              遇到\n刷新

              fflush函数强制刷新

              fclose或者程序关闭刷新

      与终端建立的缓存一般为行缓存(stdin、stdout均为行缓存)

  • 不缓存:数据立即刷新,无缓存,适用于stderr(保证错误信息及时输出)。
    • 立即刷新不缓存

      刷新条件:

              不缓存立即刷新

      出错处理或者人机交互一般使用不缓存(stderr不缓存)

注意fflush(FILE *stream)可强制刷新指定流的缓存,在实时性要求高的场景(如日志输出)中常用。

四、man 手册:查询标准 IO 的利器

在 Linux 中,man手册是查询函数用法的权威工具,标准 IO 相关函数主要分布在第 3 章节(库函数)。

1. 章节:

1. 标准命令

2. 系统调用

3. 库函数

4. 设备说明

5. 文件格式

6. 娱乐

7. 杂项

8. 管理员命令

  • 查看fopen用法:man 3 fopen

五、实战

1. 文件拷贝(fgets 与 fputs 实现)

任务:利用 fgets 和 fputs 拷贝文件

#include<stdio.h>
// 1. 利用fgets和fputs实现文件内容的拷贝int main(void)
{FILE *fp1 = NULL;FILE *fp2 = NULL;char file1[256] = {0};char file2[256] = {0};printf("请输入源文件:\n");gets(file1);printf("请输入目的文件:\n");gets(file2);fp1 = fopen(file1,"r");if(fp1 == NULL){perror("fail to fopen");return -1;}fp2 = fopen(file2,"w");if(fp2 == NULL){perror("fail to fopen");return -1;}char tmpbuff[1024] = {0};char *pret = 0;while(1){pret = fgets(tmpbuff,sizeof(tmpbuff),fp1);if(NULL == pret){break;}fputs(tmpbuff,fp2);}fclose(fp1);fclose(fp2);return 0;
}

结果:

2. 带记忆功能的数字平均值计算

任务:从终端接收数字,实时显示所有数字的平均值;程序关闭后重启,仍能显示历史所有数字的平均值。

// 2. 从终端接收数字,将数字存放在文件中,程序实时显示当前所有数字的平均数,
//如果程序关闭,再次启动时应该能够显示之前用户输入的所有数字的平均值(具有记忆功能)
// 1
// 2
// 3
// 4
// 5// 平均值:3.00
#include<stdio.h>int main(void)
{FILE *fp = NULL;float num = 0.0;int pret = 0;float sum = 0.0;int cnt = 0;float avg = 0.0;fp = fopen("a.txt","r");if(NULL == fp){perror("fail to fopen");return -1;}if(EOF == fscanf(fp,"%f",&num)){printf("当前文档内无数据\n");}else{   sum = num;cnt = 1;while(1){pret = fscanf(fp,"%f",&num);if(EOF == pret){break;}sum += num;cnt++;}avg = sum / cnt;printf("[历史记录] %d 个数据,平均值: %.3f\n",cnt,avg);}fclose(fp);//本次输入部分fp = fopen("a.txt","a");if(NULL == fp){perror("fail to fopen");return -1;}printf("请输入数字(输字母退出):\n");  //if (scanf("%lf", &number) != 1) break; //'1' 返回成功读取的变量数量while(1){   ///fscanf 返回成功读取的变量数量//输入字母时fscanf(stdin, "%f",&num)返回0(匹配失败)pret = fscanf(stdin,"%f",&num); if(EOF == pret || 0 == pret)break;fprintf(fp,"%f\n",num);sum += num;cnt++;avg = sum / cnt;printf("此为第%d个数据,当前平均值为: %.3f\n",cnt,avg);}fclose(fp);return 0;
}

结果:

  

diff:

http://www.dtcms.com/a/326523.html

相关文章:

  • 文件夹生成器,一键批量生成,效率提升!
  • 1.Apollo Planning 模块总结
  • 一键生成AI视频!Spring Cloud微服务架构的AgentAlVideo平台开源啦!
  • 内存问题排查工具ASan初探
  • 【liunx】web高可用---nginx
  • AR 智能眼镜:从入门到未来
  • DDIA第五章:复制
  • PCB批量线路板厂家有哪些?
  • LAMPLNMP 最佳实践
  • Ubuntu 22.04 离线环境下 Python 包与 FFmpeg 安装全攻略​
  • 嵌入式Linnux学习 -- 软件编程2
  • linux下部署 dify,并配置本地ollama大模型
  • 【排序算法】⑤冒泡排序
  • Java学习 -- 可变参数与Collections工具类
  • docker安装Engine stopped
  • AI自动生成接口测试脚本全流程
  • 当AI重塑世界:普通人如何成为“主动进化者”?
  • 在 .NET Core 5.0 中启用 Gzip 压缩 Response
  • ECCV 2024 论文解读丨具身智能、机器人研究最新突破创先点分享合集
  • MCU中的存储器映射(Memory Map)
  • 登录系统英文使用 Sign In?Log In?还是 Log On?
  • Windows Server 2022域控制器部署与DNS集成方案
  • 大模型工程问题
  • Python网络爬虫(一) - 爬取静态网页
  • 打烊:餐厅开业前的“压力测试”
  • nginx 设置二级目录-实战
  • P1967 [NOIP 2013 提高组] 货车运输【题解】
  • 当智慧在腕间流转:一场无声的协同交响
  • haproxy 2.4.x, /metrics 取数据遇到的问题
  • 项目代码涉及的知识点笔记整理