20-C语言:第21~22天笔记

C语言:第21~22天笔记

内容提要

数据块读写(二进制文件访问)
fread()

函数原型:

c 复制代码
 #include <stdio.h>
 size_t fread(void *ptr, size_t size, size_t count, FILE *fp);

功能描述:

从文件流中读取数据块到内存缓冲区。主要用于二进制文件的读取操作。

参数说明:

参数 类型 说明
ptr void* 指向存储读取数据的内存缓冲区指针
size size_t 每个数据项的字节大小(例如:sizeof(int))
count size_t 要读取的数据项个数
fp FILE* 指向要读取的文件流指针

返回值:

  • 成功返回实际读取的数据项个数(不是字节数)
  • 如果返回值小于count,可能表示:
    • 到达文件末尾(可用feof()检查)
    • 发生读取错误(可用ferror()检查)
fwrite()

函数原型:

c 复制代码
 size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp);

功能描述:

将内存中的数据块写入到文件流中。主要用于二进制文件的写入操作,是fread()的配套函数。

参数说明:

参数 类型 说明
ptr const void* 指向要写入数据的内存缓冲区指针
size size_t 每个数据项的字节大小(例如:sizeof(int))
count size_t 要写入的数据项个数
fp FILE* 指向要写入的文件流指针

返回值:

  • 成功返回实际写入的数据项个数(不是字节数)
  • 如果返回值小于count,表示写入过程发生错误
案例:结构体读写
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 创建学生结构体
typedef struct Student
{
	int id;           // 学号
	char name[20];    // 姓名
	float scores[3];  // 三门成绩
} Stu;

/**
 * @brief 从文件读取二进制数据
 * 
 * @return 
 */
void read_data(int len)
{
	// 打开文件
	FILE *fp = fopen("stu_data.dat", "rb");
	if (!fp)
	{
		perror("文件打开失败!");
		return;
	}
	Stu n_stus[len];
	// 从二进制文件中读取数据
	size_t read_count = fread(n_stus, sizeof(Stu), len, fp);
	// 读取失败校验
	if (read_count < len)
	{
		if (feof(fp)) printf("只读取到%lu条记录!\n", read_count);
		else /* else if(ferror(fp)) */
		{
			perror("读取文件时发生错误!");
			// 关闭文件
			fclose(fp);
			return;
		}	
	}
	
	// 关闭文件
	fclose(fp);
	
	for (size_t i = 0; i < len; i++)
	{
		printf("学生%d:%s,语文-%.2f,数学-%.2f,英语-%.2f\n", n_stus[i].id, n_stus[i].name,n_stus[i].scores[0],n_stus[i].scores[1],n_stus[i].scores[2]);
	}
	
}

/**
 * @brief 向文件写入二进制数据
 * 
 * @param stus 
 * @param len 
 * 
 * @return 
 */
int write_data(Stu *stus, int len)
{
	// 打开文件(文件不存在就创建)
	FILE *fp = fopen("stu_data.dat","wb");
	if (!fp)
	{
		perror("文件打开失败!");
		return EXIT_FAILURE;
	}
	
	// 将stus以二进制形式写入到stu_data.dat文件
	size_t written = fwrite(stus, sizeof(Stu), len, fp);
	// 写入结果校验
	if(written < len)
	{
		if (feof(fp)) printf("只写入了%lu条记录!\n", written);
		else perror("文件写入失败!");
		// 关闭文件
		fclose(fp);
		return EXIT_FAILURE;
	}
	printf("成功写入%lu条记录!\n", written);
	// 关闭文件
	
	return EXIT_SUCCESS;	
}

int main(int argc, char *argv[])
{
	
	// 准备一个学生数组
	Stu stus[] = {
		{1, "张三", {89,88,97}},
		{2, "李四", {87,86,67}},
		{3, "王五", {81,83,87}}
	};
	int len = sizeof(stus)/sizeof(stus[0]);
	
	// 本地持久化(写入)
	write_data(stus,len);
	
	// 本地持久化(读取)
    read_data(len);

	printf("\n");
	
	return 0;
}

文件的随机访问

核心函数
  1. rewind(fp) - 重置到文件开头
  2. fseek(fp, offset, whence)- 定位文件指针
    • whence取值:
      • SEEK_SET:文件开头
      • SEEK_CUR:当前位置
      • SEEK_END:文件末尾
  3. ftell(fp) - 获取当前位置
  4. feof(fp) - 检测文件结束
案例
随机访问案例
c 复制代码
#include <stdio.h>

int main(int argc, char *argv[])
{
	FILE *fp = fopen("demo01.c", "r+");
	if (!fp)
	{
		perror("文件打开失败!");
		return -1;
	}
	
	// 移动到文件末尾获取大小
	fseek(fp,0,SEEK_END);
	long size = ftell(fp);// 获取文件大小
	printf("文件大小:%ld字节\n",size);
	
	// 移动中间内容
	fseek(fp,size/2,SEEK_SET);
	// 读取文件内容
	char buf[256];
	while(!feof(fp))// 如果文件未读完,就循环读取
	{
		fgets(buf, sizeof(buf), fp);
		// 打印读取的内容
		printf("中间内容:%s\n",buf);
	}
	
	fclose(fp);
	
	return 0;
}
结构体读写
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 创建学生类型(结构体)
typedef struct Student
{
	int id;          // 学号
	char name[20];   // 姓名
	float scores[3]; // 三门成绩
} Stu;

/**
 * 向文件写入二进制数据
 */
int write_data(FILE **fp)
{
	// 将stus写入到stu_data.dat
	// 准备要写入的学生数据
	Stu stus[3] = {
		{1, "张三", {89, 88, 97}},
		{2, "李四", {87, 86, 67}},
		{3, "王五", {81, 83, 87}}};
	
	// 向文件写入二进制数据
	size_t written = fwrite(stus, sizeof(Stu), sizeof(stus) / sizeof(stus[0]), *fp);
	if (written < 3)
	{
		perror("数据写入失败!");
		fclose(*fp);
		return EXIT_FAILURE;
	}
	
	printf("成功写入%lu条学生记录\n", written);
	
	return EXIT_SUCCESS;
}

/**
 * 读取二进制文件数据
 */
int read_data(FILE **fp)
{
	// 重置文件指针都开头位置
	// rewind(*fp);
	fseek(*fp, 0, SEEK_SET);
	
	Stu stus[3];
	// 从二进制文件中读取信息
	size_t read_count = fread(stus, sizeof(Stu), sizeof(stus) / sizeof(stus[0]), *fp);
	// 读取失败
	if (read_count < 3)
	{
		if (feof(*fp)) // 查看是否读取到文件末尾
		{
			printf("只读取到%lu条记录!\n", read_count);
		}
		else
		{
			perror("读取文件时发生错误!");
		}
	}
	// 读取成功,遍历
	for (size_t i = 0; i < read_count; i++)
	{
		printf("学生%d:%s,语文-%.2f,数学-%.2f,英语-%.2f\n", stus[i].id, stus[i].name, stus[i].scores[0], stus[i].scores[1], stus[i].scores[2]);
	}
	
	return EXIT_SUCCESS;
}

int main(int argc, char const *argv[])
{
	// 打开文件(文件不存在就创建)
	FILE *fp = fopen("stu_data.dat", "rb+");// 对二进制文件读写
	if (!fp)
	{
		perror("文件打开失败!");
		return EXIT_FAILURE;
	}
	
	// 本地化保存数据(持久化保存数据)
	write_data(&fp);
	
	// 本地化数据读取(持久化数据读取)
	read_data(&fp);
	
	// 本地化数据读取(持久化数据读取)
	read_data(&fp);
	
	// 关闭文件
	fclose(fp);
	return 0;
}
相关推荐
做个文艺程序员2 分钟前
第02篇:搭建 ES 集群 + Spring Boot 整合实战——从 Docker Compose 到 Java 客户端全覆盖
java·spring boot·elasticsearch
Jinkxs3 分钟前
LoadBalancer- 简单限流策略:Nginx 基于连接 / 请求的限流实现
java·运维·nginx
fenglllle10 分钟前
JDK8升级JDK17使用CompletableFuture在线程中classloader的变化
java·开发语言·jvm
计算机安禾11 分钟前
【c++面向对象编程】第44篇:typename与class的区别,依赖类型名与template消除歧义
java·jvm·c++
流浪00111 分钟前
告别静态打印:Linux C 实现实时刷新进度条
linux·运维·c语言
Hua-Jay13 分钟前
OpenCV联合C++/Qt 学习笔记(二十五)----监督学习聚类及K均值聚类
c++·笔记·opencv·学习·计算机视觉·聚类
JAVA面经实录91720 分钟前
Java+SpringAI企业级实战项目完整官方文档(生产终版)
java·开发语言·spring·ai编程
梵得儿SHI20 分钟前
Java IO 流进阶:Buffer 与 Channel 核心概念解析及与传统 IO 的本质区别
java·开发语言·高并发·nio·channel·buffer·提升io效率
2301_7890156225 分钟前
C++_string增删查改模拟实现
java·开发语言·c++
没有逆称26 分钟前
Java OOM 问题全解析
java·jvm