C语言——文件操作(2)

耕耘 :C、C++、嵌入式技术领域

🔥我的个人主页

❄️个人专栏:《C语言专栏》 《嵌入式专栏》

✨**不要等待机会,而要创造机会!**✨

📽博主简介:

✨✨一位热爱生活的阳光大男孩.✨✨

前言

本文系统讲解C语言文件操作核心知识,涵盖文件概念、二进制与文本文件区别、流与文件指针机制,重点解析fopen、fclose及常见错误处理。通过实例演示fseek、ftell、rewind实现文件随机读写,强调打开文件后判断返回值、关闭时置空指针等最佳实践。深入剖析文件缓冲区原理及fflush强制刷新机制,阐明更新模式(如w+)下读写切换的注意事项,为掌握高效、安全的文件操作提供完整技术框架。

文章目录

  • 前言
  • [6. ⽂件的随机读写](#6. ⽂件的随机读写)
    • [6.1 fseek](#6.1 fseek)
    • [6.2 ftell](#6.2 ftell)
    • [6.3 rewind](#6.3 rewind)
  • [7. ⽂件缓冲区](#7. ⽂件缓冲区)
    • [7.1 fflush](#7.1 fflush)
  • [8. 更新⽂件](#8. 更新⽂件)
  • 结语

6. ⽂件的随机读写

6.1 fseek

c 复制代码
int fseek ( FILE * stream, long int offset, int origin );

功能:根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。

参数:

• stream :指向 FILE 类型结构体的指针,指定了要定位⽂件指针的⽂件流。

• offset :偏移量,这个偏移量要根据第三个参数来确定取值,可以是正数(向⽂件末尾⽅向移

动)、负数(向⽂件开头⽅向移动)或 0(不移动)。

• origin :偏移的起始点(基准位置),这个参数有三个取值:

◦ SEEK_SET :⽂件开始位置

◦ SEEK_CUR :⽂件指针当前的位置

◦ SEEK_END :⽂件末尾位置

返回值:

• 成功:如果⽂件位置指针被成功移动, fseek 返回 0。

• 失败:如果发⽣错误(例如试图移动到⼀个不存在的负偏移位置),则返回 ⾮零值(通常是 -1)。

使⽤注意事项:

• ⼆进制⽂件 vs ⽂本⽂件:

◦ 对于⼆进制⽂件, fseek 的⾏为⾮常直观,可以精确地移动到任何字节位置。

◦ 对于⽂本⽂件, fseek 的⾏为在某些环境下可能不确定(因为换⾏符的转换等问题)。

唯⼀被严格保证的⽤法是:

▪ 将 offset 设置为 0, origin 设置为 SEEK_SET (即移动到⽂件开头)。

▪ 将 offset 设置为 ftell() 的返回值( ftell 返回当前指针位置)。

例⼦:

c 复制代码
**/* fseek example */
#include <stdio.h>
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "wb");
	fputs("This is an apple.", pFile);
	fseek(pFile, 9, SEEK_SET);
	fputs(" sam", pFile);
	fclose(pFile);
	return 0;**

6.2 ftell

c 复制代码
long int ftell ( FILE * stream );

功能:返回⽂件指针相对于起始位置的偏移量

参数:

• stream :指向⼀个已打开⽂件的⽂件指针。

返回值:

• 成功:返回当前⽂件位置指针相对于⽂件开头的偏移量(字节数),类型为 long int 。

• 失败:如果发⽣错误(例如⽂件流⽆效或⽂件不⽀持定位),则返回 -1L。

使⽤注意事项:

• ⼆进制⽂件 vs ⽂本⽂件:

◦ 对于⼆进制⽂件, ftell 的返回值就是相对于⽂件开头的精确字节数,⾮常可靠。

◦ 对于⽂本⽂件,由于不同系统对换⾏符的处理不同(如Windows是"\r\n",Linux是"\n"),ftell 的返回值可能不直接等于从⽂件开头计数的字符数。但在⼤多数实现中,它仍然可以安全地与 fseek 配合使⽤来记录和恢复位置。

例⼦:

c 复制代码
/* ftell example : getting size of a file */
#include <stdio.h>
int main()
{
	FILE* pFile;
	long size;
	pFile = fopen("myfile.txt", "rb");
	if (pFile == NULL)
		perror("Error opening file");
	else
	{
		fseek(pFile, 0, SEEK_END); // non-portable
		size = ftell(pFile);
		fclose(pFile);
		printf("Size of myfile.txt: %ld bytes.\n", size);
	}
	return 0;
}

6.3 rewind

c 复制代码
void rewind ( FILE * stream );

功能:让⽂件指针的位置回到⽂件的起始位置

参数: stream :指向⼀个已打开⽂件的⽂件指针。

使⽤注意事项:

• 清除错误标志:

◦ rewind 不仅移动⽂件指针,还会清除⽂件的错误标志和⽂件结束标志。

◦ 这意味着即使在读取⽂件时遇到了错误或到达了⽂件末尾,调⽤ rewind 后,⽂件状态会被重置,可以重新开始操作。

• 与 fseek 的区别:

◦ fseek(fp, 0, SEEK_SET) 只移动⽂件指针,不清除错误标志。

◦ rewind(fp) 移动⽂件指针并清除错误标志。

例⼦:

c 复制代码
/* rewind example */
#include <stdio.h>
int main()
{
	int n;
	FILE* pFile;
	char buffer[27];
	pFile = fopen("myfile.txt", "w+");
	for (n = 'A'; n <= 'Z'; n++)
		fputc(n, pFile);
	rewind(pFile);
	fread(buffer, 1, 26, pFile);
	fclose(pFile);
	buffer[26] = '\0';
	printf(buffer);
	return 0;
}

7. ⽂件缓冲区

ANSI C 标准采⽤"缓冲⽂件系统" 处理数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块"⽂件缓冲区"。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。

7.1 fflush

c 复制代码
int fflush ( FILE * stream );

功能:强制刷新参数 stream 指定流的缓冲区,确保数据写⼊底层设备。

• 对输出流:将缓冲区中未写⼊的数据⽴即写⼊⽂件。

• 对输⼊流:⾏为由具体实现决定,⾮C语⾔标准⾏为(可能清空输⼊缓冲区)

• 对输⼊流 ⾏为由具体实现决定,⾮ 语⾔标准⾏为(可能清 输⼊缓冲 )

• 参数为 NULL 时:刷新所有打开的输出流

参数:

stream :指向⽂件流的指针(如 stdout 、⽂件指针等)

返回值:成功返回 0 ,失败返回 EOF

注意事项:

  1. 仅对输出流或更新流(最后⼀次操作为输出)有明确刷新⾏为
  2. 输⼊流的刷新⾏为不可移植(如清空输⼊缓冲区是⾮标准特性)
  3. 程序正常终⽌( exit )或调⽤ fclose 时会⾃动刷新,但程序崩溃时缓冲区数据可能丢失
    代码演⽰:
c 复制代码
#include <stdio.h>
#include <windows.h>
//VS2022 WIN11环境测试
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
//注:fflush 在⾼版本的VS上不能使⽤了
printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭⽂件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}

这⾥可以得出⼀个结论:

因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。

如果不做,可能导致读写⽂件的问题。

8. 更新⽂件

在⽂件的打开模式中有三种⽅式值得注意,分别是: "r+", "w+", "a+",分别是什么意思?

关键要点:

1.在写完⽂件后,要继续读⽂件的时候,在读取之前⼀定要使⽤ fflush() 刷新⽂件缓冲区,再使⽤ fseek() , rewind() 重新定位⽂件指⽰器的位置。

2.在读完⽂件后,需要继续写⽂件之前,在写⽂件之前,使⽤ fseek() , rewind() 重新定位⽂件指⽰器的位置。

c 复制代码
#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "w+");
	if (fp == NULL)
	{
		perror("fopen for w+");
		return 1;
	}
	//写abcdefghi到⽂件中
	fputs("abcdefghi", fp);
	//刷新缓冲区,保证数据写⼊⽂件
	fflush(fp);
	//要读取数据b字符,先定位⽂件指针
	fseek(fp, 1, SEEK_SET);
	int ch = fgetc(fp);//读取字符
	printf("%c\n", ch);
	//在b的位置开始写⼊hello
	fseek(fp, -1, SEEK_CUR);
	//解释:因为前⾯读取⼀个字符后,⽂件指⽰器现在指向了c,需要从当前位置退回⼀个字符
	fputs("hello", fp);
	//关闭⽂件
	fclose(fp);
	fp = NULL;
	return 0;
}

结语

愿你收获满满,点赞、收藏、转发三连不断,好运常伴!

相关推荐
纪念 2291 小时前
C++类和对象(最终篇)
开发语言·c++
程序员yu1 小时前
会编网络:别被入门教程骗了,Python实用能力不在语法本身
开发语言·python·学习·算法
Lintongzg1 小时前
千卡训练的隐形账单:通信不是瓶颈
笔记·学习·性能优化·llm
Bruce_Liuxiaowei1 小时前
Python 字典默认值完全指南:从 KeyError 到 defaultdict
开发语言·数据结构·python·字典
troy1282 小时前
Java 技术栈全景指南:从基础到微服务的完整知识体系
java·开发语言·微服务
LuminousCPP2 小时前
从零开始学 C++(三):类和对象入门|从 struct 到 class,理解封装、对象与 this 指针
c++·笔记·学习·类和对象
2601_967212722 小时前
国际品质电源轨道系统供应商技术评估维度
大数据·网络·经验分享
Escalating_xu2 小时前
【C 语言】深入理解指针(2):数组名、数组传参、二级指针与指针数组
java·c语言·开发语言
郝学胜-神的一滴2 小时前
游戏引擎原理与实践 01:聊聊游戏引擎的前世今生
开发语言·c++·游戏引擎·产品运营·软件工程·产品经理·untiy