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

嵌入式第二十五课!!!文件读写操作补充与应用、以及文件流定位相关函数接口

文件读写操作补充

fgets/fputs

这个函数用来读写文本文件,不能对二进制文件进行读写;

在使用 gets 和 fgets 时,需要注意的是:

fgets:
1. 从指定的已打开文件中读取最多一行数据(遇到\n停止读取)
2. fgets保留\n字符并字符串末尾添加\0
3. fgets最多读取size-1个字符,最后一个位置存放\0

gets:
1. 指定从终端设备读取数据
2. gets会将终端读到的\n字符替换成\0;
3. gets是危险的,因为在读取时,没有大小的限制,可能造成内存越界。

如下图所示,二者读取方式不同:

Linux操作系统默认已打开的三个文件

FILE *stdin:标准输入流 ——>标准输入设备:键盘
FILE *stdout:标准输出流——>标准输出设备:显示屏
FILE *stderr:标准出错流——>标准出错设备:显示屏

举个例子:

int ret = fgetc(stdin) 等价于 int ret = getchar( );

fwrite函数与fread函数

fwrite函数

fwrite 函数是这样使用的:

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

功能:向文件中写入nmemb个大小是size的数据到文件中
参数:
        ptr : 要写入的数据的首地址
       size:每个元素的字节数
       nmemb:要写入的元素个数
       stream:要写入的文件流指针
返回值:
      成功:返回实际写入的元素个数

因为fwrite函数可以传参读取数据的元素字节大小,故 fwrite 函数(以及fread函数)是用来读(写)固定大小的二进制数据(比如说结构体就可以通过这个函数读取)

#include <stdio.h>struct stu
{int id;char name[32];int score;
};int main(int argc, const char *argv[])
{FILE *fp = fopen("./1.txt", "w");if (NULL == fp){printf("fopen error\n");return -1;}
/*int num = 100;int a[5] = {100,200,300,400,500};size_t cnt  = fwrite(&num, sizeof(int), 3, fp);printf("cnt = %ld\n", cnt);
*/struct stu s = {1, "zhangsan", 99};struct stu ss[5] = {{1, "zhangsna", 99}, {2, "lisi", 88},{3, "wanger", 67}, {4, "maliu", 56},{5, "zhaowu", 100}};size_t cnt = fwrite(&s, sizeof(struct stu), 1, fp);printf("cnt = %ld\n", cnt);cnt = fwrite(ss, sizeof(struct stu), 5, fp);printf("cnt = %ld\n", cnt);fclose(fp);return 0;
}

运行结果如下:

fread函数

fread函数是这样使用的:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取nmemb个大小是size的元素
参数:
        ptr:存储读取到数据的首地址
        size:每个元素的大小
        nmemb:希望从文件中读取的元素个数
       stream:要读的文件流指针
返回值:
       成功:实际读到的元素个数
       读到文件末尾:0

举个例子:

#include <stdio.h>struct stu
{int id;char name[32];int score;
};int main(int argc, const char *argv[])
{FILE *fp = fopen("./1.txt", "r");if (NULL == fp){printf("fopen error\n");return -1;}struct stu s;struct stu ss[10];size_t cnt = fread(&s, sizeof(struct stu), 1, fp);printf("cnt = %ld\n", cnt);printf("%d %s %d\n", s.id, s.name, s.score);cnt = fread(ss, sizeof(struct stu), 10, fp);printf("cnt = %ld\n", cnt);for (int i = 0; i < cnt; i++){printf("%d %s %d\n", ss[i].id, ss[i].name, ss[i].score);}fclose(fp);return 0;
}

运行结果如下:

文件读写操作应用练习:

1. 读取一个bmp文件的头部,打印该bmp文件的分辨率和像素深度

bmp文件的构成是这样的:

故我们在读取bmp文件的图片信息前,要先读取掉前14个字节的文件相关信息,让文件流指针指到图片相关信息部分,后再进行读取;

厘清思路,我们来对bmp的存储相关信息结构体开始定义:

#include <stdio.h>#pragma pack(1)//bmp文件相关信息
typedef struct tagBITMAPFILEHEADER {short    bfType;         // 文件类型标志int      bfSize;         // 文件大小,单位为字节short    bfReserved1;    // 保留字节short    bfReserved2;    // 保留字节int      bfOffBits;      // 数据偏移量,即实际图像数据开始的位置
}Bmp_file_head_t;
//bmp图像信息
typedef struct tagBITMAPINFOHEADER {int   biSize;         // BITMAPINFOHEADER的大小,单位为字节int    biWidth;        // 位图的宽度,单位为像素int    biHeight;       // 位图的高度,单位为像素short    biPlanes;       // 目标设备的位平面数,必须为1short    biBitCount;     // 每像素位数(颜色深度)int   biCompression;  // 图像压缩类型int   biSizeImage;    // 图像大小,单位为字节int    biXPelsPerMeter;// 水平分辨率,单位为像素/米int    biYPelsPerMeter;// 垂直分辨率,单位为像素/米int   biClrUsed;      // 实际使用颜色数int   biClrImportant; // 重要颜色数
}Bmp_info_t;

(ps:此处对于bmp文件的相关信息来源于深入解析BMP文件格式及读写操作-CSDN博客)

在定义结构体之前,使用 “  pragma pack (1)  ”来让结构体按1字节对齐,防止数据错乱;

int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)
{FILE *fp = fopen(bmpname, "r");if (NULL == fp){printf("fopen error\n");return -1;}fread(pheadinfo, sizeof(Bmp_file_head_t), 1, fp);fread(pbmpinfo, sizeof(Bmp_info_t), 1, fp);fclose(fp);return 0;
}
int main(int argc, const char *argv[])
{Bmp_file_head_t headinfo;Bmp_info_t bmpinfo;get_bmp_head_info("./3.bmp", &headinfo, &bmpinfo);printf("sizeof(Bmp_file_head_t) = %ld\n", sizeof(Bmp_file_head_t));printf("sizeof(Bmp_info_t) = %ld\n", sizeof(Bmp_info_t));printf("biWidth = %d, biHeight = %d, biBitCount = %d\n", bmpinfo.biWidth, bmpinfo.biHeight, bmpinfo.biBitCount);return 0;
}

此后,正常打开bmp文件信息,先读前14个字节的 file_head_t文件,再读40个字节的file_info_t文件,后打印出结构体中的这些信息(分辨率与像素深度);
 

流定位相关接口

fseek和ftell

fseek 函数是这样使用的:

int fseek(FILE *stream, long offset, int whence);
功能:实现文件流重新定位
参数:
        stream:需要定位的文件流指针
        offset:偏移量
        whence:定位的相对位置
                 SEEK_SET : 从头进行偏移
                 SEET_CUR:从当前位置开始偏移
                 SEET_END: 从文件末尾偏移
返回值:
      成功:返回当前的偏移量
      失败:-1(EOF)

ftell 函数是这样使用的:

 long ftell(FILE *stream);
功能:获取流的当前位置到文开头的偏移量
参数:
         stream:文件流
返回值:
       偏移量:byte

举个例子:


#include <stdio.h>int main(int argc, const char *argv[])
{FILE *fp = fopen("./1.txt", "w");if (NULL == fp){printf("fopen error\n");return -1;}fseek(fp, 10, SEEK_SET);fputc('A', fp);long offset = ftell(fp);printf("offset = %ld\n", offset);fseek(fp, 5, SEEK_CUR);fputc('B', fp);offset = ftell(fp);printf("offset = %ld\n", offset);fseek(fp, -2, SEEK_CUR);fputc('C', fp);fseek(fp, 4, SEEK_END);fputc('D', fp);fseek(fp, -10, SEEK_END);fputc('E', fp);fclose(fp);return 0;
}

运行结果为:

rewind

rewind 是这样使用的:

void rewind(FILE *stream);
功能:流复位函数(复位到开头)
等价于:fseek(fp, 0, SEEK_SET);  

练习题目:

1.获取文件的大小(字节数)

#include <stdio.h>int main(int argc, const char *argv[])
{FILE *fp = fopen("1.txt", "w");	if (NULL == fp){printf("fopen error\n");return -1;}fseek(fp, 0, SEEK_END);long len = ftell(fp)printf("%ld\n",len);fclose(fp);return 0;
}

先把指针移到文件的末尾,后使用 ftell 计算此时离文件开头的偏移量(即字节数)

2.模拟迅雷下载(抢占磁盘空间)

所谓的抢占磁盘空间,即是在硬盘里下载文件前,先产生一个空洞文件,占据下载内容大小的空间;如果硬盘大小不够,及时提醒用户硬盘空间不足;

模拟下载这个过程总共分为三步:

     0.获取源文件大小
     1. 抢占目标文件的磁盘空间
     2. 将数据拷贝到目标磁盘对应的位置(fread/fwrite实现文件拷贝)

#include <stdio.h>
#include <stdlib.h>int main(int argc, const char *argv[])
{if (argc < 3){printf("Usage : ./a.out <srcfile> <dstfile>\n");return -1;}FILE *fpsrc = fopen(argv[1], "r");if (NULL == fpsrc){printf("fopen error srcfile\n");return -1;}//1、 获取源文件大小fseek(fpsrc, 0, SEEK_END);long len = ftell(fpsrc);rewind(fpsrc);//2、抢占目标文件的磁盘空间FILE *fpdst = fopen(argv[2], "w");if (NULL == fpdst){printf("fopen error dstfile\n");return -1;}fseek(fpdst, len-1, SEEK_SET);int ret = fputc('\0', fpdst);if (EOF == ret){printf("磁盘空间不足\n");fclose(fpsrc);fclose(fpdst);return -1;}rewind(fpdst);	//3、数据拷贝char buff[1024] = {0};while (1){size_t cnt = fread(buff, 1, sizeof(buff), fpsrc);if (0 == cnt){break;}fwrite(buff, 1, cnt, fpdst);}fclose(fpsrc);fclose(fpdst);return 0;
}

以上就是今天和大家分享的内容!!!!感谢你的阅读!!!如有遗漏和错误,欢迎评论区进行指正!!!

http://www.lryc.cn/news/617574.html

相关文章:

  • idea没有 Active Profiles 的填写位置
  • 企业级 IT 运维服务平台数据备份方案:基于 rsync 的自动化实现
  • 视觉相机偏移补偿
  • 在macOS上扫描192.168.1.0/24子网的所有IP地址
  • 初识影刀:将多个相同格式EXCEL中内容汇总到一个EXCEL文件中去
  • Camera open failed
  • 在Linux中模拟配置高性能web服务器
  • gophis钓鱼
  • 掌握while循环:C语言编程基础
  • [动态规划]最长公共子序列(LCS)
  • golang 基础案例_01
  • Go选手如何快速上手第三方库
  • Springboot-vue 地图展现
  • JDK21虚拟线程和 Golang1.24协程的比较
  • 《姜妮与Veda的最后一次传输》
  • 李宏毅2025《机器学习》-第十讲:AI“思想钢印”:深入解析大模型的知识编辑技术
  • 秒懂边缘云|1分钟了解边缘安全加速 ESA
  • 机器学习——K-means聚类
  • 第9节 大模型分布式推理核心挑战与解决方案
  • 数据备份与进程管理
  • 机器学习:基于OpenCV和Python的智能图像处理 实战
  • 芯片设计流程
  • C# 异步编程(计时器)
  • 【C++】封装哈希表模拟实现unordered_set和unordered_map
  • Android 16 的用户和用户组定义
  • 基于倾斜摄影三维模型影像提取水面
  • Spring源码解析 - SpringApplication run流程-prepareContext源码分析
  • 了解不同电磁仿真类型中的电容报告
  • 某地渣库边坡自动化监测服务项目
  • GDB调试 core dump 文件与栈溢出分析