C语言文件操作 文件操作指针的偏移、文件大小等
在上一篇文章中,我们讨论了C语言文件操作中的文件的打开、关闭、读取,也给出了相应实例。
本文就接着上一篇文章最后所提出的问题继续探讨关于文件操作指针的问题。
文章目录
1. 文件操作指针偏移
1.1 偏移操作
说是说指针偏移,将其称之为“寻找操作位置”也未尝不可,也更加容易记住这个函数。为什么这么说呢,寻找 ----- seek,不难猜出这个函数就是 fseek。
来看看函数原型:
int fseek(FILE *fp, long offset, int origin);
@fp: 要偏移操作位置的文件指针
@offset: 偏移大小
@origin: SEEK_SET 0 开头
SEEK_CUR 1 当前
SEEK_END 2 末尾
返回值:成功返回0,失败返回-1。
关于第二参数 offset ,我们注意一下他的类型是 long 而不是 unsigned long ,这说明这个参数可能是负数,那就很奇怪了呀,可以使正数、负数、0,那怎么确定是哪个呢?
那如果我告诉你这个正负性是指偏移方向,负数向左,正数向右偏移的话,你能想到什么呢?
没错,这个参数是基于第 3 个参数的,在第 3 个参数的位置的基础上前后移动 offset 个字节。
做个试验
假设我有一个 a.txt 文件,里面存储着万年新手代码 hello world。
那么下面的语句读取到的是什么内容呢?
if(!fseek(fp, 0, SEEK_SET))
{
if(fread(buffer, 1, 5, fp) > 0)
{
printf("1. buffer = %s\n", buffer);
}
}
if(!fseek(fp, -5, SEEK_CUR))
{
if(fread(buffer, 1, 5, fp) > 0)
{
printf("2. buffer = %s\n", buffer);
}
}
if(!fseek(fp, -6, SEEK_END))
{
if(fread(buffer, 1, 5, fp) > 0)
{
printf("3. buffer = %s\n", buffer);
}
}
- buffer = hello
- buffer = hello
- buffer = world
分析一下
第一个大 if 的意思是:从头开始,不加左右偏移,读取 5 个字节存储在 buffer 中。
第二个大 if 执行时,当前文件操作指针在 hello 的 o 之后,再读取内容时就是空格开始了。此时的 fseek 语句是指:指针先保持当前位置不动,然后加上向前偏移 5 个字节开始读取 5 个字节内容。
第三个大 if 的意思就跟简单了,偏移至最后之后,已经读取不到内容了,已经到了文档末尾(end of file),也就是再头文件中定义的 EOF 宏值。在这个基础上向前偏移 6 个字节读取 5 个字节。
是不是觉得不够直观,想要看到直接一点的数据?这不,他来了。
1.2 获取当前操作位置与文件开头之间的字节数
函数原型:
long ftell(FILE *fp);
返回值:成功返回当前操作位置与开头的字节数,失败返回-1;
嗯,这个函数多简单是吧,我们把这个函数加入到上面的代码中。
就是这样了,这个小函数还是比较简单的。
其实基于本文的当前内容已经可以做一个小小的面试题了:
1.3 面试题:给出一个文件名,返回该文件的字节数(文件大小)。
相信有童鞋已经想到了,将文件操作指针偏移至文件末尾,再用 ftell 获取其与文件开头的值即可。
代码如下:
long getFileSize(const char *file_name)
{
if(file_name == NULL)
{
printf("file name error!\n");
return -1;
}
FILE *fp = fopen(file_name, "r");
if(fp == NULL)
{
printf("Open file failed!\n");
return -1;
}
if(!fseek(fp, 0, SEEK_END)) {
return ftell(fp);
}
else
{
return -1;
}
}
到这里应该能够理解 fseek 的使用了吧,那我们再顺便提一提上面提到的 EOF。
2. 文件操作位置是否到达了文档末尾?
当我们使用 fread 去读取文件时,相信大家还记得我们是如何判断操作是否成功的对吧。不过呢,不知道各位有没有注意到有个特殊情况 ------- 文件操作位置位于文档末尾。由于此时的位置之后已经没有内容可读了,fread 自然而然返回了 0,而我们却把它当做错误处理掉了,想想好像有点不太合适?
那我们如何判断呢?锵锵,就是这个宏 ---- EOF,全称为:end of file。
同时,还有两个小函数一并解决了吧:
int feof(FILE *fp);
返回值:成功返回真;失败返回假。
int ferror(FILE *fp);
返回值:成功返回真;失败返回假。
不作过多解释了,依旧是那个只有 “hello world.” 的 a.txt 文件,加上这两个保险的话就是这样子滴:
int main()
{
FILE *fp = fopen("C:\\Users\\Axian\\Desktop\\a.txt", "r");
if(fp == NULL)
{
printf("Open file failed!\n");
return -1;
}
char buf[5] = {0};
int ret;
/***********************************/
while(1) // 读完为止,或者是出错停下
{
ret = fread(buf, 1, 1, fp); // 每次读 1 个字节
if(ret == 0) // 有情况,没有读取到内容
{
if(feof(fp)) // 到文档末尾了吗?
{
printf("End of file, nothing to read.\n");
fclose(fp);
return 0;
}
if(ferror(fp)) // 没有到文档末尾多半就是出错啦,不过既然说到这个函数就给个面子吧。
{
printf("Error during read file!\n");
fclose(fp);
return -1;
}
}
printf("Read buf = %s\n", buf); // 看看每次读取到了什么?
}
// 不管是出错还是读取完毕,关闭文件的操作必不可少,养成好习惯哦。
/****************************************/
return 0;
}
运行结果:
Read buf = h
Read buf = e
Read buf = l
Read buf = l
Read buf = o
Read buf =
Read buf = w
Read buf = o
Read buf = r
Read buf = l
Read buf = d
Read buf = .
End of file, nothing to read.
嗯,到这的话其实都差不多说完了,写入文件也是一样的道理,其实只要你像我这两篇文章的思路,大胆去将接口试一试,提出问题,用运行结果来佐证或是推翻你的猜测,我觉得已经没有什么是很难的了。
好吧,本文最后炒一炒冷饭,不使用文件操作指针偏移,仅使用 fread 、feof 和 ferror 来统计文件大小又该怎么写呢?
有兴趣的童鞋可以将代码贴在评论区大家一起讨论,你或许还在犹豫,我已经写完啦(笑)。
下一篇博文就把尾巴收掉吧,也不知道啥时候能更上来了,太忙(lan)了。