Linux C/C++编程:lseek、fseek、ftell、rewind、fgetpos、fsetpos、

rewindfseekftell为C库函数,有缓冲,lseek为系统函数,不带缓冲

理论

每个打开文件都有一个与其相关联的”当前文件偏移量“

  • 它通常是一个非负整数。
  • 通常,读写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数
  • 当打开文件时通常其读写位置是指向文件开头, 若是以附加的方式打开文件(如O_APPEND), 则读写位置会指向文件尾. 当read()或* write()时, 读写位置会随之增加

我们可以去获取设置这个文件偏移量

lseek

lseek()可以控制文件偏移量(文件的读写位置)

#include <sys/types.h>  
#include <unistd.h>

off_t lseek(int fildes, off_t offset, int whence);

功能:

  • 设置一个文件偏移量
  • lseek为系统函数,不带缓冲

参数:

  • fildes 为已打开的文件描述词
  • 参数 whence 为下列其中一种:
    • SEEK_SET表示文件开头,这样参数offset 即为新的读写位置.
    • SEEK_CUR表示当前位置
    • SEEK_END 表示文件末尾
  • offset 为根据参数whence来移动读写位置的位移数.

__offset与__whence有关:

  • 如果__whenceSEEK_SET,则该文件偏移量为文件头+offset个字节,offset可正可负
  • 如果__whenceSEEK_CUR,则该文件偏移量为当前值+offset个字节,offset可正可负
  • 如果__whenceSEEK_END ,则该文件偏移量为文件尾+offset个字节,offset可正可负

返回值:

  • 如果lseek成功执行,则返回新的文件偏移量。
  • 如果文件描述符指向一个管道、FIFO、tty 、或者网络套接字,则lseek返回-1, 并将errno设置为ESPIPE

错误:

  • EBADF fd不是一个打开的文件描述符。
  • EINVAL whence不是SEEK_SET,SEEK_CUR,SEEK_END其中之一;或者产生的文件偏移量是负的,或超出设备的可搜索范围。
  • OVERFLOW 由此产生的文件偏移超过了off_t。
  • ESPIPE fd是与管道,套接字,或FIFO相关的。

我们来看个例子:下面例子测试标准输入被设置偏移量

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
        printf("cannot seek\n");
    else
        printf("seek OK\n");
    exit(0);
}

调用

$ ./apud < /etc/passwd
seek OK
$ cat < /etc/passwd | ./apud 
cannot seek

注意:

  • 某些设备可能会有负的偏移量,因此比较lseek的返回值要测试它是否等于-1

lseek64

当需要在大于2G的文件中跳转或在更大的块设备中跳转的时候lseek是无法完成任务的,这需要其他的文件跳转系统调用。

Linux内核驱动函数llseek就可以完成这样的操作

#include <sys/types.h>
#include <unistd.h>
int _llseek(unsigned int fd, unsigned long offset_high,unsigned long offset_low, loff_t *result,unsigned int whence);

而lseek64是_llseek的包装函数

  #define _LARGEFILE64_SOURCE     
  /* See feature_test_macros(7) */
  #include <sys/types.h>
  #include <unistd.h>

  off64_t lseek64(int fd, off64_t offset, int whence);

注意

当对大于2G的文件或块设备文件操作时,open打开时flag上需要加上O_LARGEFILE 表示以大文件的方式打开。

添加O_LARGEFILE时编译时会出现错误

error: O_LARGEFILE undeclared (first use in this function)

解决的办法是添加宏

#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for O_DIRECT */
#endif

或者

#define __USE_FILE_OFFSET64
#define __USE_LARGEFILE64
#define _LARGEFILE64_SOURCE

注意宏添加在头文件前,不然依然报错

ftell、fseek

/*
* 功能: 设置文件位置为给定流 stream 的文件的开头
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流
*/
void rewind(FILE *stream)

#include <stdio.h>
/*
* 功能: 返回给定流 stream 的当前文件位置。
* 参数:  stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
* 返回值:返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。
*/
long int ftell(FILE *stream)

/*
* 功能: 重定位流(数据流/文件)上的文件内部位置指针
*       注意:文件指针指向文件/流。位置指针指向文件内部的字节位置,随着文件的读取会移动,文件指针如果不重新赋值将不会改变或指向别的文件。
* 参数:
* 	stream为文件指针
*   offset为偏移量,正数表示正向偏移,负数表示负向偏移
*   origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET 
* 			SEEK_SET: 文件开头
*           SEEK_CUR: 当前位置
*           SEEK_END: 文件结尾
*           其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
* 反正:  成功,返回0,失败返回非0值,并设置error的值,可以用perror()函数输出错误
*/
int fseek( FILE *stream, long offset, int origin );

附加说明:fseek()不像lseek()会返回读写位置, 因此必须使用ftell()来取得目前读写的位置.

举个例子:

#include <stdio.h>
main()
{
  FILE * stream;
  long offset;
  fpos_t pos;
  stream = fopen("/etc/passwd", "r");
  fseek(stream, 5, SEEK_SET);
  printf("offset = %d\n", ftell(stream));
  rewind(stream);
  fgetpos(stream, &pos);
  printf("offset = %d\n", pos);
  pos = 10;
  fsetpos(stream, &pos);
  printf("offset = %d\n", ftell(stream));
  fclose(stream);
}

在这里插入图片描述

fgetpos、fsetpos

/*
* 作用:  获取流 stream 的当前文件位置,并把它写入到 pos。
* 参数:
* 		stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
*       pos -- 这是指向 fpos_t 对象的指针
* 返回值: 如果成功,该函数返回零。如果发生错误,则返回非零值
*/
int fgetpos(FILE *stream, fpos_t *pos)


/*
* 作用:  设置给定流 stream 的文件位置为给定的位置
* 参数:
* 		stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
*       pos -- 这是指向 fpos_t 对象的指针,  是由函数 fgetpos 给定的位置。
* 返回值: 如果成功,该函数返回零值,否则返回非零值,并设置全局变量 errno 为一个正值,该值可通过 perror 来解释。
*/
int fsetpos(FILE *stream, const fpos_t *pos)

这些都是C标准库中对文件定位相关的函数。

ftell与fseek一起使用,fsetpos与fgetpos一起使用。ftell与fseek返回的是长整数,而后面两个则是返回一个新类型(fpos_t)

  • ftell() 和 fseek() 用长整型表示文件内的偏移 (位置), 因此, 偏移量被 限制在 20 亿 (231-1) 以内。
  • 而新的 fgetpos() 和 fsetpos() 函数使用 了一个特殊的类型定义 fpos_t 来表示偏移量。这个类型会适当选择,

因此, fgetpos() 和 fsetpos 可以表示任意大小的文件偏移。fgetpos() 和 gsetpos() 也可以用来记录多字节流式文件的状态。

举个例子:

#include <stdio.h>


int main ()
{
    FILE  *fp;
    fpos_t  fpos;

    fp = fopen("file.txt", "w+");
    fgetpos(fp, &fpos);
    fputc("Hello world\n", fp);

    fsetpos(fp, &fpos);
    fputs("这将覆盖之前的内容", fp);
    fclose(fp);

    return(0);
}

在这里插入图片描述

实践

fseek、ftell

1、获取文件长度

UINT32 filesize(FILE *fp)
{
        UINT32 fSet,fEnd,filelen;   

        fseek(fp,0,SEEK_SET);  
        fSet = ftell(fp);   

        fseek(fp,0,SEEK_END);  
          fEnd = ftell(fp);   
       
        rewind(fp);

        return (filelen = fEnd - fSet);  
}

rewind

#include <stdio.h>

int main()
{
    char str[] = "This is runoob.com";
    FILE *fp;
    int ch;

    /* 首先让我们在文件中写入一些内容 */
    fp = fopen( "file.txt" , "w+" );  /* 打开文件用于读写 */
    fwrite(str , 1 , sizeof(str) , fp );
    //  fwrite(c, strlen(c) + 1, 1, fp); 

    fread(buffer, strlen(c) + 1, 1, fp);
    printf("%s\n", buffer);
    rewind(fp);  // 
    printf("\n");
    while(1)
    {
        ch = fgetc(fp);
        if( feof(fp) )
        {
            break ;
        }
        printf("%c", ch);
    }
    fclose(fp);

    return(0);
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值