获取当前文件位置
下面的函数返回当前文件的访问位置。当需要标记文件中的位置,以便以后返回到该位置时,可以使用下面的函数。
long ftell(FILE*fp);
ftell()返回 fp 流的文件位置。对一个二进制流来说,它与该位置之前的字符数量是相同的,也就是当前字符位置距离文件头部的偏差。当发生错误时,ftell()返回 -1。
int fgetpos(FILE*restrict fp,fpos_t*restrict ppos);
fgetpos()将 fp 流的文件位置指示符写入 ppos 所引用的对象,该对象类型为 fpos_t。如果 fp 是一个宽字符导向流,那么 fgetpos()所存储的指示符也会包含流当前的转换状态。当发生错误时,fgetpos()返回非 0 值;当执行成功时,返回 0。
下面的示例记录文件 messages.txt 中以 # 字符开头的所有行的位置:
#define ARRAY_LEN 1000 long arrPos[ARRAY_LEN] = { 0L }; FILE *fp = fopen( "messages.txt", "r" ); if ( fp != NULL) { int i = 0, c1 = '/n', c2; while ( i < ARRAY_LEN && ( c2 = getc(fp) ) != EOF ) { if ( c1 == '/n' && c2 == '#' ) arrPos[i++] = ftell( fp ) - 1; c1 = c2; } /* ... */ }
设置文件访问位置
下面的函数修改文件位置指示符。
int fsetpos(FILE*fp,const fpos_t*ppos);
将文件位置指示符和转换状态设置成 ppos 所引用对象中存储的值。ppos 所引用对象内的这些值必须通过调用函数 fgetpos()才能获得。如果成功,fsetpos()返回 0,并清除该流的 EOF 标记。如果发生错误,则返回非 0 值。
int fseek(FILE*fp,long offset,int origin);
将文件位置指示符设置为以参数 origin 作为参考点,offset 作为偏差。三种可能的参考点均被定义为宏值,参数 offset 指定位置只可能是相对这三种参考点中的一种。
表 1 列出了这些宏,以及在 ANSI C 定义它们之前,曾用于 origin 的传统取值。这些 offset 值可以是负的,但是,最终结果所获得的文件位置必须大于等于 0。
宏名称 | origin的传统取值 | 偏差相对于的参考点 |
SEEK_SET | 0 | 文件开头 |
SEEK_CUR | 1 | 当前文件位置 |
SEEK_END | 2 | 文件结尾 |
当处理文本流时(在可区分文本流和二进制流的系统上),应该使用通过调用函数 ftell()获得的值作为 offset 参数,并且让 origin 的值为 SEEK_SET。
函数 ftell()与 fseek()、fgetpos()与 fsetpos()并非互相兼容的,因为 fgetpos()和 fsetpos()用来指示文件位置的 fpos_t 对象,可以不是算术类型。
如果成功的话,fseek()会清除流的 EOF 标记并返回 0。非 0 的返回值表示发生错误。函数 rewind()将文件位置指示符设置成文件开头,并清除流的 EOF 与错误标记:
void rewind( FILE *fp );
如果不考虑对错误标记的影响,那么调用 rewind(fp)等同于:
(void)fseek( fp, 0L, SEEK_SET )
如果该文件已被以读写模式打开,那么在成功调用 fseek()、fsetpos()或 rewind()之后,就可以进行读写操作。
下面的例子使用一个索引表来存储文件中记录的位置。这个方法允许直接地访问需要被更新的记录。
// setNewName():在索引表中找关键字,并且更新文件中关键字所对应的记录 // 包含这些记录的文件,必须以“读写模式”打开;也就是采用模式字符串"r+b" // 参数:—指向被打开数据文件的指针;—关键字;—新名称 // 返回值:指向更新记录的指针,当未找到时,返回NULL // --------------------------------------------------------------- #include <stdio.h> #include <string.h> #include "Record.h" // 定义类型Record_t, IndexEntry_t: // typedef struct { long key; char name[32]; // /* ... */ } Record_t; // typedef struct { long key, pos; } IndexEntry_t; extern IndexEntry_t indexTab[]; // 索引表 extern int indexLen; // 表条目的数量 Record_t *setNewName( FILE *fp, long key, const char *newname ) { static Record_t record; int i; for ( i = 0; i < indexLen; ++i ) { if ( key == indexTab[i].key ) break; // 找到指定的键 } if ( i == indexLen ) return NULL; // 没有找到 // 将文件位置设定到该记录: if (fseek( fp, indexTab[i].pos, SEEK_SET ) != 0 ) return NULL; // 定位失败 // 读取记录: if ( fread( &record, sizeof(Record_t), 1, fp ) != 1 ) return NULL; // 读取错误 if ( key != record.key ) // 测试键值 return NULL; else { // 更新记录 size_t size = sizeof(record.name); strncpy( record.name, newname, size-1 ); record.name[size-1] = '/0'; if ( fseek( fp, indexTab[i].pos, SEEK_SET ) != 0 ) return NULL; // 设定文件位置出错 if ( fwrite( &record, sizeof(Record_t), 1, fp ) != 1 ) return NULL; // 写入文件出错 return &record; } }
在写操作之前的第二个 fseek()调用,可以用下面代码替换,以相对于之前的位置,移动文件指针:
if (fseek( fp, -(long)sizeof(Record_t), SEEK_CUR ) != 0 ) return NULL; // 设定文件位置出错
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/21672.html