C语言实现随机读写文件的函数详解
作者:努力学习游泳的鱼
在上一篇博客中,我详细讲解了如何使用C语言顺序读写文件。所谓顺序读写,就是按照顺序,挨个挨个的读或者写,换句话说,我必须先读第一个,再读第二个,接着读第三个,以此类推,而不能一上来就读第五个。这样就不够灵活,因为有时候我们想读写文件的任意位置。这就需要使用文件的随机读写。
文件的随机读写,可以在文件中指定的任意位置读或者写。这是怎么做到的呢?事实上,当我们顺序读写文件时,文件中会有一个指针记录你读(或者写)到哪了。比如,一个文件里存着"abcdefg",当我们打开文件时,这个指针默认指向了第一个字符a,当你用fgetc读完a后,这个指针就指向了下一个字符b,当你再使用fgetc读取文件时就读到了b,以此类推。如果我们想一上来就读e,除了按照顺序读写的方法,把前面的abcd全都读走,还有一种方法,直接让这个指针指向e,就可以了。
所以,文件的随机读写,本质上是改变记录读写位置的指针的指向,就能够读写任意位置了。下面我们将学习与此相关的3个函数,分别是:
1.fseek,改变该指针的指向。
2.ftell,获取该指针相对于起始位置的偏移量。
3.rewind,使文件指针指向起始位置。
1.fseek
前面已经剧透过了,fseek可以改变记录读写位置的指针的指向,从而实现文件的随机读写。该函数的声明如下:
int fseek ( FILE * stream, long int offset, int origin );
第一个参数大家都很熟悉了,就是一个文件指针,告诉fseek我现在操作的是哪个文件。
第二个参数,offset是偏移量的意思,大家先记住。第三个参数,origin,顾名思义,即“起始位置”。后面两个参数让人摸不着头脑,但是配合起来就很有意思了。
举个例子:假设一个文件中存有abcdefg,当你用fopen打开这个文件时,一个记录读写位置的指针就指向了起始位置,也就是指向了a。如果我想读取第5个字符,即e呢?那我就要让这个指针指向e,这样再fgetc就能读到e了。
我可以这样算:e相对于起始位置a,向右偏移了4个字符,因为相对于a,a自己的偏移量是0,b是1,c是2,d是3,e自然是4。那么,偏移量offset(fseek的第二个参数)就是4,这个4是相对于起始位置的,这个“相对于”哪里,也就是origin(fseek的第三个参数)的位置,此时origin就是“起始位置”,明白了吧。而fseek用一个宏SEEK_SET来表示“起始位置”,所以此时fseek的调用如下:
fseek(pf, 4, SEEK_SET);
下面我们来验证一下。先在工程目录下创建一个文件,文件名是test.txt,然后在里面输入abcdefg。
按照打开文件->读文件->关闭文件的顺序,写代码:
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } fseek(pf, 4, SEEK_SET); putchar(fgetc(pf)); fclose(pf); pf = NULL; return 0; }
打开文件后,使用fseek让记录读写位置的指针指向e,接着用fgetc读取e并用putchar打印出来,输出结果如下:
我们就成功跳过了前面的abcd,直接读取到e,实现了文件的随机读写。
当然,读取到e不止有这一种写法。fseek的第三个参数origin有三种取值,分别是:
1.SEEK_SET,表示起始位置。
2.SEEK_CUR,表示当前指向的位置。
3.SEEK_END,表示文件末尾。
SEEK_SET和SEEK_CUR都很好理解,需要注意的是,SEEK_END不是指向最后一个字符,而是指向最后一个字符的下一个位置。比如,一个文件中存有abcdefg,SEEK_END不是指向g,而是指向g的下一个位置。所以同样是读到e,如果origin是SEEK_END,offset就应该是-3,因为g的偏移量是-1,f是-2,所以e是-3。把上面代码中的fseek的调用方式改一下,其他不变:
fseek(pf, -3, SEEK_END);
也可以读到e,因为在执行完上面这行代码后,记录读写位置的指针就指向了e,所以读取时就跳过了前面的abcd,直接读到e。说明一下,由于读写前该指针指向了e,调用fgetc读完e后,该指针就指向了e后面的f,也就是说,此时SEEK_CUR就指向了f,如果我们想读到b呢?可以数一下,abcdefg,b相对于SEEK_CUR(指向f)的偏移量就是-4,如果像下面的代码这样写:
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); // 指向e putchar(fgetc(pf)); // 读完e后指向f fseek(pf, -4, SEEK_CUR); // 指向b putchar(fgetc(pf)); // 读完b后指向c fclose(pf); pf = NULL; return 0; }
输出结果如下:
2.ftell
我如果想知道此时记录读写位置的指针相对于起始位置的偏移量(即相对于SEEK_SET的偏移量)是多少,就可以问问ftell函数。该函数声明如下:
long int ftell ( FILE * stream );
ftell会返回当前相对于起始位置的偏移量。比如前面的例子中调用:
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); // 指向e putchar(fgetc(pf)); // 读完e后指向f fseek(pf, -4, SEEK_CUR); // 指向b putchar(fgetc(pf)); // 读完b后指向c printf("\n%ld\n", ftell(pf)); fclose(pf); pf = NULL; return 0; }
由于此时记录文件读写位置的指针指向了c,偏移量是2(a为0,b为1,c为2),所以会输出2。
3.rewind
rewind的作用是,让指向文件读写位置的指针指向文件的起始位置。该函数声明如下:
void rewind ( FILE * stream );
其实非常简单,rewind(pf);和fseek(pf, 0, SEEK_SET);是等价的。
在前面的例子中,记录文件读写位置的指针左右横跳,最后指向了c,此时ftell也返回了2,这时如果我rewind一下,该指针就又指向了起始位置,即a,再ftell就能知道偏移量回到了0,如果再fgetc,就能重新读取到a了。代码如下:
#include <stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); // 指向e putchar(fgetc(pf)); // 读完e后指向f fseek(pf, -4, SEEK_CUR); // 指向b putchar(fgetc(pf)); // 读完b后指向c printf("\n%ld\n", ftell(pf)); rewind(pf); printf("%ld\n", ftell(pf)); putchar(fgetc(pf)); // a fclose(pf); pf = NULL; return 0; }
输出结果:
当然,把上述代码中的rewind(pf);换成fseek(pf, 0, SEEK_SET);的效果是一样的。
总结
1.文件的随机读写本质上是改变一个指向文件读写位置的指针的指向。
2.使用fseek和rewind函数可以做到(1)这一点。其中,fseek需要知道该指针需要指向的新的位置,具体需要一个相对于某起始位置(origin)的偏移量(offset),其中origin可以取值为SEEK_SET(文件的起始位置),SEEK_END(文件的结束位置)和SEEK_CUR(当前位置)。rewind函数会让该指针直接指向SEEK_SET,所以rewind(pf);等价于fseek(pf, 0, SEEK_SET);。
3.使用ftell函数可以获取当前该指针相对于起始位置的偏移量。
到此这篇关于C语言实现随机读写文件的函数详解的文章就介绍到这了,更多相关C语言随机读写文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!