C语言⽂件操作讲解(3)
目录
前言
一、⽂件的随机读写
介绍:
一、fseek函数
介绍:
二、ftell函数
介绍:
三、rewind函数
介绍:
二、⽂件读取结束的判定
介绍:
一、fgetc()(单个字符读取)函数 "r"
二、fgets()(字符串读取)函数 "r"
三、feof()函数判定错误 "r"
三、⽂件缓冲区
介绍:
一、fflush函数
总结
前言
C语言⽂件操作讲解(1~2)文章讲解了为什么使⽤⽂件、什么是⽂件、⼆进制⽂件和⽂本⽂件、⽂件的打开和关闭、⽂件的顺序读写知识的相关内容,而⽂件的随机读写、⽂件读取结束的判定、⽂件缓冲区为本章节知识的内容。
一、⽂件的随机读写
介绍:
指的是通过 fseek()、ftell()、rewind() 函数控制文件指针位置,实现非顺序读写。(至于这3个函数,接下来我会一一讲解)
一、fseek函数
介绍:
函数原型:
int fseek ( FILE * stream, long int offset, int origin );
int fseek(FILE *stream, long int offset, int origin) 是C语言标准库中用于定位文件指针的函数,需包含头文件 <stdio.h>。
功能:根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。
参数讲解:
- stream:指向 FILE 结构体的指针(文件指针),标识要操作的文件。
- offset:偏移量(字节数),可正可负:
正数:从 origin 位置向文件末尾方向移动。
负数:从 origin 位置向文件开头方向移动。
- origin:基准位置,必须是以下宏之一(定义在 <stdio.h> 中):
origin基准位置的可能情况:
宏名 数值 含义 SEEK_SET
0 指向文件开头(起始位置) SEEK_CUR
1 指向当前文件指针所在位置 SEEK_END
2 指向文件末尾
你在写origin基准位置值时候可以选择写宏名或写该宏名对应的数值。
举个例子:
fseek(fp, 5, SEEK_SET);
结合知识,举个结合该函数的例子:
(date.txt文件中我预先存储了:Alice 20 95.500000内容)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main() {FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}char c = fgetc(file);printf("%c ", c);fseek(file, 2, 0);c = fgetc(file);printf("%c ", c);fclose(file);return 0;
}
结合实例:
讲解一下:
- 第一次读取(偏移0):
fgetc(file)
读取文件第一个字符'A'
(正确)。fseek(file, 2, 0)
:从开头偏移2字节,指向第三个字符(原内容前两个字符为'A'
和'l'
,偏移2后指向'i'
)。- 第二次读取(偏移2):
fgetc(file)
读取'i'
,最终输出A i
。
结合实例2:
讲解一下:
(date.txt文件中我还是预先存储了:Alice 20 95.500000内容:是按照结构体 char name[20];
int age; float score; 顺序存储的)
#include <stdio.h>
#include <stdlib.h>
struct Student
{char name[20];int age;float score;
};
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){perror("文件打开失败");return 1;}struct Student s;int ret = fscanf(file, "%s %d %f", s.name, &s.age, &s.score);if (ret == 3){printf("姓名:%s\n年龄:%d\n成绩:%.2f\n", s.name, s.age, s.score);}else{printf("文件格式错误,需满足:姓名 年龄 成绩(空格分隔)\n");}fclose(file);return 0;
}
解释一下:
在main中先通过struct Student s创建s,通过int ret = fscanf(file, "%s %d %f", s.name, &s.age, &s.score);来接取文件中的值。
- 返回值
ret
:成功读取的变量个数(如读取3个值则返回3);失败或文件结束返回EOF
(-1)。- 参数传递:
s.name
是数组名(本身是地址),无需&
;&s.age
、&s.score
需取地址。而通过printf("姓名:%s\n年龄:%d\n成绩:%.2f\n", s.name, s.age, s.score); 语句打印fscanf函数时存入的值。
二、ftell函数
介绍:
函数原型:
long int ftell ( FILE * stream );
ftell 函数是 C 语言标准库 <stdio.h> 中的函数,用于返回当前文件指针在文件中的字节偏移量(即距离文件开头的字节数)。
功能:即返回⽂件指针相对于起始位置的偏移量。
参数讲解:
- stream:指向 FILE 结构体的指针(文件指针),标识要操作的文件。
举个例子:(date.txt文件中我预先存储了:Alice 20 95.500000内容)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}int postion = ftell(file);printf("%d ", postion);char c = fgetc(file);postion = ftell(file);printf("%d ", postion);fseek(file, 2, 0);c = fgetc(file);postion = ftell(file);printf("%d ", postion);fclose(file);return 0;
}
解析:
文件打开与初始位置
- fopen("date.txt", "r") 以只读模式打开文件,成功后文件指针指向 文件开头(位置0)。
- 第一个ftell(file) 返回当前位置:0 → 输出 0 。
读取一个字符后fgetc(file) 读取第一个字符(如'a'),文件指针自动后移1位。
第二个ftell(file) 返回当前位置:1 → 输出 1 。
偏移后读取字符fseek(file, 2, 0):从 文件开头(0) 偏移2字节,指针移动到 位置0 + 2 = 2。
fgetc(file) 读取位置2的字符(如'c'),指针后移1位,即到达3处。
第三个ftell(file) 返回当前位置:3 → 输出 3 。
三、rewind函数
介绍:
函数原型:
void rewind ( FILE * stream );
功能:即将文件指针
stream
重新定位到 文件开头(位置0),同时清除文件错误标志和EOF标志。
该函数使用场景:
- 重复读取文件:无需关闭再打开,直接重置指针到开头。
- 清除错误状态:若之前读取到EOF或发生错误,
rewind()
可重置状态以便重新操作。
参数讲解:
- stream:指向 FILE 结构体的指针(文件指针),标识要操作的文件。
注意点:
根据函数功能,我们可知晓:
rewind(stream) ≈ fseek(stream, 0, SEEK_SET);
但两函数之间也有区别:
fseek()
会返回状态码(0成功,非0失败),而rewind()
无返回值。rewind()
会额外调用clearerr() (
清除stream
错误和EOF的函数)
,清除stream
的错误标志和EOF标志。
举个例子:(date.txt文件中我预先存储了:Alice 20 95.500000内容)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}int postion = ftell(file);printf("%d\n", postion);char c = fgetc(file);printf("%c ", c);rewind(file);postion = ftell(file);printf("%d \n", postion);fseek(file, 2, 0);c = fgetc(file);printf("%c ", c);rewind(file);postion = ftell(file);printf("%d ", postion);fclose(file);return 0;
}
结果为:
改代码讲解:
- 首先:使用fopen("date.txt", "r")以只读模式打开文件,若打开失败则输出提示并退出,在此基础上可以访问date.txtw文件中的内容了。
从上到下第一个ftell(file)获取初始文件指针位置(默认为0,即文件开头),并打印,因为文件刚打开时,指针默认在开头,故输出 0。
getc(file):读取后指针自动后移1字节(即指向下一个字符), printf("%c ", c);值为‘A’。经过rewind函数重置指针到开头成功将指针重新定位到开头,故从上到下第二个postion = ftell(file);
printf("%d \n", postion); 输出值为 0。下面有:fseek(file, offset, origin):
offset=2:偏移量(字节数)。
origin=0:基准位置(0=SEEK_SET文件开头,1=SEEK_CUR当前位置,2=SEEK_END文件结尾)。
效果:指针从开头移动2字节,指向第3个字符(索引0开始)。
此时getc(file):读取后指针自动后移1字节(即指向下一个字符), printf("%c ", c);值为‘i’,经过rewind函数重置指针到开头成功将指针重新定位到开头,最后一个postion = ftell(file);
printf("%d \n", postion); 输出值也为 0。
二、⽂件读取结束的判定
介绍:
在C语言中,文件读取结束的判定需结合读取函数的返回值和文件结束标志(EOF),避免因读取错误或数据巧合等于EOF导致误判,接下来我将讲解一些文件函数读取结束的判定。
一、fgetc()(单个字符读取)函数 "r"
int fgetc ( FILE * stream ); C语言⽂件操作讲解(2)中有讲
- 返回值:成功时返回读取的字符;读取结束或失败时返回
EOF
(宏定义,值为-1
)。- 注:要用int类型接收fgetc函数的返回值,避免char无法存储EOF的问题。
举例:(设该文件中已经有"Alice", 20, 95.5f内容了)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}int a;while ((a = fgetc(file))!= EOF){putchar(a); }fclose(file);return 0;
}
运行结果为:
结合代码返回值为0,我们可知,程序运行正确。
二、fgets()(字符串读取)函数 "r"
char * fgets ( char * str, int num, FILE * stream );C语言⽂件操作讲解(2)中有讲
- 返回值:成功时返回存储字符串的地址;读取结束(无数据)或失败时返回
NULL
。
举例:(设该文件中已经有"Alice", 20, 95.5f内容了)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}char a[100];while (fgets(a, sizeof(a), file)!= NULL){printf("%s", a);}fclose(file);return 0;
}
结果为:
如果 while (fgets(a, sizeof(a), file)!= NULL)改成 while (fgets(a, sizeof(a), file))则会导致死循环错误,所以要记住fgets(a, sizeof(a), file)!= NULL。
三、feof()函数判定错误 "r"
int feof(FILE *stream); C语言⽂件操作讲解(1)中有讲
- 非零值:文件流已到达末尾或发生读取错误(需结合
ferror
判断具体原因)。- 0:文件流未到达末尾。
注:用法:
feof()
用于 区分“读取结束”和“读取错误”((需配合ferror()
)检测是否有错误)
举例:(设该文件中已经有"Alice", 20, 95.5f内容了)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
int main()
{FILE* file = fopen("date.txt", "r");if (file == NULL){printf("打开失败\n");return;}int a;while ((a = fgetc(file)) != EOF){putchar(a);}printf("\n");if (feof(file)){printf("读取成功\n");}else if (ferror(file)){printf("读取失败\n");}fclose(file);return 0;
}
结果:
可知晓结果。
三、⽂件缓冲区
介绍:
从定义上来看:ANSIC标准采⽤“缓冲⽂件系统”处理数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲 区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊ 到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。
简单来说:
想象你去超市购物,购物袋就是“文件缓冲区”,超市货架是“磁盘文件”,你的家是“程序内存”。
- 如果没有购物袋,你买一件东西就跑一趟家(直接读写磁盘),会累到崩溃(效率极低)。
- 有了购物袋,你可以先把所有东西放进袋子(数据暂存缓冲区),最后一次性拎回家(装满后统一写入磁盘),省时省力(减少磁盘操作次数,提高效率)。
总的来说:缓冲文件系统就是用“临时袋子”暂存数据,凑够一批再统一搬运,避免频繁“跑腿”,让数据读写更快更高效。
但如果我们往文件缓冲区中存入一部分数据后,文件缓冲区没有满的情况下,要将数据读写到磁盘中,我们可以使用fflush函数的:
一、fflush函数
int fflush ( FILE * stream );
功能:
功能:强制刷新参数 stream 指定流的缓冲区,确保数据写⼊底层设备。
参数:
- stream:指向 FILE 结构体的指针(文件指针),标识要操作的文件。
- 对输出流:将缓冲区中未写⼊的数据⽴即写⼊⽂件。
- 对输⼊流:⾏为由具体实现决定。
- 若参数为 NULL 时:刷新所有打开的输出流。
- 返回值:成功返回 0 ,失败返回 EOF。
总结
以上就是今天要讲的内容,本篇文章涉及的C语言⽂件操作讲解的知识点:⽂件的随机读写、⽂件读取结束的判定、⽂件缓冲区为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位,接下来新的知识内容我会很快更新,接下来我将会开始更新数据结构的知识,目前以C的风格为主,我认为C语言是C++的基础,所以打好C语言知识内容,对学习C++语言有更大的帮助,希望大家能喜欢我的文章,谢谢各位,接下来新的知识内容我会很快更新。