标准I/O文件操作

标准I/O文件

文件基础概念

文件定义

存储在外存储器(磁盘、U盘、移动硬盘等)上的数据集合,是操作系统管理数据的基本单位。

文件操作核心

  • 文件内容的读取(输入操作 Input)
  • 文件内容的写入(输出操作 Output)

文件缓冲机制

  • 系统为文件在内存中创建缓冲区(通常4KB大小)
  • 程序操作实际上是在缓冲区进行
  • 数据像水流一样流动,称为“文件流”
  • 缓冲机制减少了直接访问外存的次数,提高效率

文件分类

  1. 文本文件(ASCII文件)
    • 以ASCII码形式存储
    • 可直接用文本编辑器查看
    • 示例:.txt、.c、.h文件
  2. 二进制文件
    • 以二进制形式存储
    • 需要特定程序才能解析
    • 示例:.exe、.jpg、.dat文件

文件标识

  1. 文件系统中:路径 + 文件名

    • Windows示例:D:\edu\test.txt
    • Linux示例:/home/user/data.bin
  2. C程序中:文件指针(FILE *类型)

    FILE *fp;   // 声明文件指针
    

文件操作的基本步骤

  1. 打开文件:建立程序与文件的连接
  2. 文件处理/读写文件:读写操作
  3. 关闭文件:释放资源

文件打开与关闭

fopen()

说明:打开文件

函数原型:

#include <stdio.h>

FILE *fopen(const char *path, const char *mode);

参数详解:

  • path:文件路径(绝对路径或相对路径)

  • mode:打开模式

    • 基本模式(ASCII模式):

      • r 以只读方式打开文件,文件指针指向文件起始位置。
      • r+ 以读写方式打开文件,文件指针指向文件起始位置。
      • w 以写的方式打开文件,如果文件存在则清空,不存在就创建,文件指针指向文件起始位置。
      • w+ 以读写方式打开文件,如果文件不存在则创建,否则清空,文件指针指向文件起始位置。
      • a 以追加方式打开文件,如果文件不存在则创建,文件指针指向文件末尾位置。
      • a+ 以读和追加方式打开文件,如果文件不存在则创建,如果读文件则文件指针指向文件起始位置,如果追加(写)则文件指针指向文件末尾位置。
    • 二进制模式:

      • rbwbab等,其实就是基本模式的前提下,加b
    • 特殊模式:

      • x:独占模式(与w组合,文件必须不存在)

返回值:

  • 成功:返回文件指针(指向FILE结构体)
  • 失败:返回NULL(需要检查errno)

错误处理示例:

FILE *fp = fopen("data.txt","r");
if(fp == NULL)
{
    perror("文件打开失败!");
    // 退出进程
    exit(EXIT_FAILURE); // exit(1)   程序失败退出
}

fclose()

**说明:**关闭文件

函数原型:

#include <stdio.h>

int fclose(FILE *fp);

参数

  • fp(文件指针):指向要关闭的 FILE 对象的指针,通常由 fopen() 或类似函数返回。

返回值

  • 成功:返回 0(即 EXIT_SUCCESS)。
  • 失败:返回 EOF(通常是 -1),并设置 errno 指示错误原因(如磁盘已满、文件被占用等)。

注意事项:

  • 必须关闭所有打开的文件
  • 程序退出时未关闭的文件可能丢失数据
  • 多次关闭同一文件指针会导致未定义行为

完整示例

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    // 打开文件
    FILE *fp = fopen("texst.txt","w");
    if (!fp)
    {
        perror("打开文件失败!");
        return -1; // exit(1);
    }
    // 读写文件
    // 关闭文件
    if(fclose(fp) != 0)
    {
        perror("关闭文件失败!");
        return -1;
    }
    return 0;
}

文件顺序读写

单字符读写

fgetc()

**说明:**单字符的读取

函数原型:

int fgetc(FILE *fp);

参数说明:

  • fp:待操作的文件指针

返回值:

  • 成功:返回字符的ASCII码
  • 失败:或者文件末尾返回EOF(-1)
fputc()

**说明:**单字符的写出

函数原型:

int fputc(int c, FILE *fp);

参数说明:

  • c:待写出的字符的ASCII码
  • fp:待操作的文件指针

返回值:

  • 成功:返回写入的字符的ASCII码
  • 失败:返回EOF(-1)
案例:文件拷贝(字符版)
#include <stdio.h>

int main(int argc,char *argv[])
{
    if (argc != 3)
    {
      printf("用法:%s 源文件 目标文件\n", argv[0]);
      return -1;
    }

    // 打开代操做的文件
    FILE *src = fopen(argv[1],"r");
    FILE *dest = fopen(argv[2],"w");

    // 非空校验
    if (!src || !dest)
    {
      perror("文件打开失败!");
      return -1;
    }

    // 读写文件
    int ch;

    while((ch = fgetc(src)) != EOF) // 循环读取 EOF:-1 返回-1,表示读取结束
    {
      // 循环的写入
      fputc(ch, dest);
    }

    // 给文件末尾添加换行
    fputc('\n', dest);

    // 关闭文件
    fclose(src);
    fclose(dest);
    
    return 0;
}

行读写(多字符读写)

fgets()

**说明:**读取字符串(字符数组)

函数原型:

#include <stdio.h>

char* fgets(char *buf, int size, FILE *fp);

功能描述:

从指定的文件流(fp)中读取一行字符串(以 \n 结尾),并将其存储到缓冲区 buf 中。

  • 读取的字符数最多为 size - 1(保留一个位置给 \0)。
  • 如果遇到 换行符 \n文件结束符 EOF,则停止读取。
  • 读取的字符串末尾会自动添加 \0(空字符),使其成为合法的 C 字符串。

参数:

  • buf(缓冲区):用于存储读取数据的字符数组(必须足够大)。
  • size(最大读取长度):最多读取 size - 1 个字符(防止缓冲区溢出)。
  • fp(文件指针):要读取的文件流(如 stdinfopen() 返回的指针等)。

返回值

  • 成功:返回 buf 的指针(即读取的字符串)。
  • 失败或到达文件末尾(EOF):返回 NULL
fputs()

**说明:**写入字符串

函数原型:

int fputs(const char *str, FILE *fp);

功能描述:

将字符串 str 写入到指定的文件流 fp 中。

  • 不自动添加换行符:与 puts() 不同,fputs 不会在末尾自动添加 \n
  • 遇到 \0 停止:写入过程遇到字符串结束符 \0 时终止(\0 本身不会被写入)。

参数:

  • str:要写入的字符串(必须以 \0 结尾)。
  • fp:目标文件流(如 stdout、文件指针等)。

返回值:

  • 成功:返回一个非负整数(通常是 0 或写入的字符数)。
  • 失败:返回 EOF(-1),并设置 errno 指示错误原因(如磁盘已满、文件不可写等)。
案例:文件拷贝(行处理)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LEN 256

int main(int argc,char *argv[])
{
    if (argc != 3)
	{
		printf("用法:%s 源文件 目标文件\n", argv[0]);
		return -1;
	}
	
	// 打开文件
	FILE *src = fopen(argv[1],"r");
	FILE *dest = fopen(argv[2],"w");
	
	// 非空校验
	if (!src || !dest)
	{
		perror("文件打开失败!");
		return -1;
	}
	
	// 创建一个数组,用来存储每次读写的文本数据、
	char line[MAX_LEN];
	// 循环读取数据
	while(fgets(line, MAX_LEN - 1, src)) // NULL:读取失败,或者读取完毕
	{
		// 去除换行符
		// line[strcspn(line,"\n")] = '\0';  // 将\n替换\0
		printf("读取行:%s\n", line);
		// 写入数据到文件
		if (fputs(line, dest) < 0) // -1:写入失败,或者写入完毕
		{
			printf("文件写入完毕!\n");
			return 1;
		}
	}
	
	// 关闭文件
	fclose(src);
	fclose(dest);
	
    return 0;
}

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

fread()

函数原型:

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

功能描述:

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

参数说明:

参数类型说明
ptrvoid*指向存储读取数据的内存缓冲区指针
sizesize_t每个数据项的字节大小(例如:sizeof(int))
countsize_t要读取的数据项个数
fpFILE*指向要读取的文件流指针

返回值:

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

函数原型:

 size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp);

功能描述:

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

参数说明:

参数类型说明
ptrconst void*指向要写入数据的内存缓冲区指针
sizesize_t每个数据项的字节大小(例如:sizeof(int))
countsize_t要写入的数据项个数
fpFILE*指向要写入的文件流指针

返回值:

  • 成功返回实际写入的数据项个数(不是字节数)
  • 如果返回值小于count,表示写入过程发生错误
案例:结构体读写
#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) - 检测文件结束
案例
随机访问案例
#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;
}
结构体读写
#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;
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值