C文件操作2:如何随机的进行文件读取?

上篇C文件操作1:如何写入读取?fopen的6种组合参数怎么用?介绍了C语言文件操作的基本函数,fopen、fwrite、fread、fclose。这些只能从文件头读写或文件尾追加写入。

本篇介绍文件中随机位置读写的方法,会介绍fseek、ftell、rewind。

此外,再介绍几个字符读写函数:fputs、fgets、fpritf、fscanf,用于编写测试代码时用。

文件随机位置读写基础函数

对于文件的随机位置读写,可以通过 fseek 、ftell与rewind 函数来完成

fseek

fseek用于设置流stream的文件读写位置为给定的偏移

seeK的中文含义是“寻找”

函数原型:

/** @func:  fseek
*   @brief: 设置流stream的文件读写位置为给定的偏移
*   @para:  [fp]:文件指针
*           [offset]:偏移量,表示移动的字节数,正数表示正向(结尾)偏移,负数表示负向(开头)偏移
*           [from]:表示设定从文件的哪里开始偏移,取值范围如下表所示
*   @return:执行成功,返回0 (fp将指向以from为基准,偏移offset个字节的位置)
*           执行失败,返回值-1,并设置errno的值。比如offset超过文件自身大小,则不改变fp指向的位置
*/
int fseek(FILE *fp,long offset,int from);

from参数的取值

起始点 表示 符号 数字表示
文件开头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

例如:

  • 将读写位置移动到文件开头
fseek(fp, 0L, SEEK_SET)
  • 将读写位置移动到文件末尾
fseek(fp,0L,SEEK_END);
  • 将读写位置移动到离文件开头100字节
fseek(fp,100L,SEEK_SET);
  • 将读写位置移动到离文件当前位置100字节
fseek(fp,100L,SEEK_CUR);
  • 将读写位置退回到离文件结尾100字节处(offset为负数表示向开头处移动)
fseek(fp,-100L,SEEK_END);

注意:

fseek 函数一般用于二进制文件,当然也可以用于文本文件。

当fseek函数操作文本文件时,要注意回车换行的情况

因为在一般浏览工具(如 UltraEdit)中,回车换行被视为两个字符 0x0D 和 0x0A,但真实的文件读写和定位却按照一个字符 0x0A 进行处理。

这种清空,可以先将文件整个读入内存,然后在内存中手工插入 0x0D。

ftell

fseek 函数只返回执行的结果是否成功,并不返回文件的读写位置

获取当前文件的读写位置,还需要使用 ftell 函数来获取

函数原型:

/** @func:  ftell
*   @brief: 得到文件当前的位置指针相对于文件首的偏移字节数
*   @para:  [fp]:文件指针
*   @return:
*/
long ftell(FILE *fp);

fell的主要作用就是获取当前的读写位置,在随机方式存取文件时,由于文件位置频繁前后移动,程序不容易确定文件的当前位置。

在使用 fseek 函数移动了位置后,再调用函数 ftell 就能非常容易地确定文件的当前位置。

fell的一个小应用:获取文件的长度

加入一共文件的读写位置已经被移动了多次,这时若想获得文件的长度,可以向用ftell记录当前的读写位置,然后将其移动到末尾,再利用ftell获取文件尾至头部的位置,就是文件的长度了。获取程度之后,再将读写位置使用fseek复原即可。

long getFileLength(FILE *fp)
{
    long curPos=0L;/*文件当前的位置指针位置*/
    long len=0L;
    curPos = ftell(fp);/*记录文件当前的位置指针的位置*/
    fseek(fp, 0L, SEEK_END);/*读写位置移动到文件末尾*/
    len = ftell(fp);/*获取文件末尾到文件开头的长度*/
    fseek(fp, curPos, SEEK_SET);/*再将读写位置移回到之前的位置*/
    return len;
}

代码对应的设计思路如下图:

C文件操作2:如何随机的进行文件读取?

rewind

rewind的中文意思是“倒回”

rewind 函数用于将文件内部的位置指针重新指向一个流(数据流或者文件)的起始位置。

注意,这里的“指针”表示的不是文件指针,而是文件内部的位置指针。即随着对文件的读写,文件的位置指针(指向当前读写字节)向后移动。而文件指针指向整个文件,如果不重新赋值,文件指针不会发生改变。

函数原型:

/** @func:  rewind
*   @brief: 将文件内部的位置指针重新指向一个流(数据流或者文件)的起始位置
*   @para:  [fp]:文件指针
*   @return:无
*/
void rewind(FILE *fp);

由于 rewind 函数没有返回值,所以很难判断rewind(fp)是否执行成功。

因此,应该尽量使用 fseek 来替换 rewind 函数,从而以验证流已经成功地回绕

文件读取写入字符串

fputs

fputs函数用于将一行字符串写入文件

函数原型:

/** @func:  fputs
*   @brief: 将一行字符串写入文件
*   @para:  [str]:要写入的字符串
*           [fp]:文件指针
*   @return:写入成功,返回非负数
*           写入失败,返回EOF
*/
int fputs( char *str, FILE *fp );

fgets

fgets 函数用来从指定的文件中读取一个字符串,并保存到字符数组中

函数原型:

/** @func:  fgets
*   @brief: 从指定的文件中读取一个字符串,并保存到字符数组中
*   @para:  [str]:字符数组
*           [n]:要读取的字符数目
*           [fp]:文件指针
*   @return:读取成功,返回字符数组首地址,也即str
*           读取失败,返回 NULL
*/
char *fgets ( char *str, int n, FILE *fp );

fprintf

函数原型:

/** @func:  fprintf
*   @brief: 将格式化的字符串写入文件
*   @para:  [fp]:文件指针
*           [format]:格式化字符串,要被写入到fp中的文本
*   @return:写入成功,返回写入的字符数
*           写入失败,返回负数
*/
int fprintf(FILE *fp, const char *format, ...)

使用方法:

FILE *fp = fopen ("test.txt", "w+");
char *str = "xxpcb.gitee.io";
int num = 666;
fprintf(fp, "%s %d", str, num);
fclose(fp);

fscanf

函数原型:

/** @func:  fscanf
*   @brief: 从文件中读取格式化的字符串
*   @para:  [fp]:文件指针
*           [format]:格式化字符串,从fp中读出的内容
*   @return:读取成功,返回读出的字符数
*           读取失败,返回负数
*/
int fscanf(FILE *fp, const char *format, ...)

使用方法:

FILE *fp = fopen ("test.txt", "r");
char str[64]
int num;
fscanf(fp, "%s %d", str, num);
fclose(fp);
使用示例

下面的测试程序,首先使用fputs函数写入了一段字符串“Hello world”,然后使用fseek函数,将读写位置移动到了文件开头向后的第6个字符,接着在该处,又使用fputs函数写入了一段字符串“xxpcb.github.io”,这样,就会在指定位置处,进行覆盖写入。最后,使用fgets函数,将文件中写入的内容再获取出来。

#include <stdio.h>
#define N 100

int main ()
{ 
    /*打开*/
    FILE *fp = fopen("../test-futs.txt","wt+");/*打开一个文件*/
    if(NULL == fp)
    {
        printf("open file fail\r\n");
        goto end;
    }
    
    /*写入*/
    fputs("hello world\n", fp);/*先写入一段信息*/
    fseek( fp, 6, SEEK_SET );/*读写位置从开头向后移动6个位置*/
    fputs("xxpcb.github.io\n", fp);/*再写入一段信息*/
    if(0 != fputs("码农爱学习\n", fp));/*再写入一段信息*/
    {
    	printf("fputs file ok\r\n");
	}

    
    /*关闭*/
    fclose(fp);
    
    /*再打开*/
    fp = fopen("../test-futs.txt","rt");
    if(NULL == fp)
    {
        printf("open file fail\r\n");
        goto end;
    }
    
    /*读取*/
    printf("fgets file ...\r\n");
    char str[N+1];
    int line = 0; 
    while(fgets(str, N, fp) != NULL)/*使用while, 可以一行一行的获取*/
    {
        printf("[%d]:%s", ++line, str);
    }
    
end:
   return(0);
}

代码对应的设计思路如下图:

C文件操作2:如何随机的进行文件读取?

附:本篇以及上篇的测试代码,可从我的gitee仓库获取(地址:https://gitee.com/xxpcb/c-test/tree/master/C-file-operate),或公众号后台回复“C文件操作”获取~

N, fp) != NULL)/使用while, 可以一行一行的获取/
{
printf("[%d]:%s", ++line, str);
}

end:
return(0);
}


代码对应的设计思路如下图:

[外链图片转存中...(img-BbNhuyTT-1620401372436)]

附:本篇以及上篇的测试代码,可从我的gitee仓库获取(地址:https://gitee.com/xxpcb/c-test/tree/master/C-file-operate),或公众号后台回复“**C文件操作**”获取~


 

上一篇:关于C语言中fseek函数的使用


下一篇:fseek函数会刷新C缓冲区中的数据吗?