系列文章目录
目录
前言
在(一)文章中我们介绍了,文件的基本概念,知道了在程序设计中我们一般分俩种文件:程序文件和数据文件,然后介绍了文件名。同时我们重点介绍了数据文件中的文本文件的操作。
学会了打开文件和关闭文件,以及各种顺序读写的函数。在本篇文章中我们要介绍文件的随机读写。
一、文件的随机读写
通过上一章的知识我们知道文件的打开是使用fopen传递文件的初始地址(及指针)。我们可以想象为光标一个一个地将文件里的内容读取。那我们该如何随心所欲的读取任意位置的数据呢?我们下面来介绍文件随机读写的函数。
1.1 fseek
根据文件指针的位置和偏移量来定位文件指针。
int fseek ( FILE * stream, long int offset, int origin );
fseek有三个参数,第一个参数是我们文件的指针,第二个是我们要设置的偏移量,第三个是其实位置。其中第三个参数只有三种选择
SEEK_SET是文件开始位置。如果选择它,那么会根据开始位置开始偏移。假设第二个参数是4,就是相对与起始位置向右偏移4个数据。
SEE_CUR是根据你所操作之后,当前光标的位置进行偏移,假设你已经将光标进行操作向右移了4个了,你要回到起始位置第二个参数就是-4,如果要往右进行则是正整数。
SEEK_END是文件结束位置。如果选择它,那么会根据结束位置开始偏移。假设第二个参数是-4,就是相对与结束位置向左偏移4个数据。
我们拿下面的代码举例子:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
文件data.txt中的数据是abcdefghi运行代码的结果是:
那么光标的位置就是下图:
我们要回到起始位置的话有三种模式:
1.选择SEEK_SET,及从开始位置开始偏移,那么偏移量就是0;
fseek(pf,0,SEEK_SET).用法如下面的代码:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, 0, SEEK_SET);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
2.选择SEEK_CUR,从当前位置开始偏移,因为是向左偏移所以偏移量是-4.,
将上面的代码改成如下就行:
fseek(pf, -4, SEEK_CUR);
3.选择SEEK_END,及从文件结束位置开始偏移,向左偏移-9;
fseek(pf, -9, SEEK_END);
以上结果均是:
。
1.2 ftell
ftell返回文件指针相对于起始位置的偏移量,用它可以知道当前位置。
long int ftell ( FILE * stream );
用法如下:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
int ret = ftell(pf);
printf("%d\n", ret);
fseek(pf, -9, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行的结果是:
可以知道相对于起始位置的偏移量,然后通偏移量进行操作。
1.3 rewind
让文件指针的位置回到文件的起始位置。
void rewind ( FILE * stream );
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行的结果是:
二、文件读取结束的判定
feof是用来判定文件是否结束的。
int feof ( FILE * stream );
文件读取结束的判定通常有以下两种方式:
-
使用feof()函数判断文件是否已经读取到结尾。feof()函数会返回一个非零值表示已经读取到文件结尾,否则返回0。
-
在读取文件时,可以使用fgets()函数读取文件中的每一行,当fgets()函数返回NULL时,表示已经读取到文件结尾。
2.1 被错误使用的 feof
牢记:在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。
feof的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
ferror的作用是:用于检查文件流是否发生了错误。当文件流发生了错误时,ferror() 函数会返回非零值,否则返回 0
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如:
• fgetc 判断是否为 EOF .(fgetc读取错误或者读取到文件末尾都会返回EOF)
• fgets 判断返回值是否为 NULL .(fgets读取错误或者读取到文件末尾都会返回EOF)
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
fread判断返回值是否小于实际要读的个数。
文件文本的例子:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,⾮char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制文本的例子:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
三、 文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
#include <stdio.h>
#include <windows.h>
//VS2019 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语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
如果不做,可能导致读写文件的问题。
总结
以上就是c语言中的文件内容,如果你有收获可以留下你宝贵的关注和点赞。谢谢你的光看!