上篇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;
}
代码对应的设计思路如下图:
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);
}
代码对应的设计思路如下图:
附:本篇以及上篇的测试代码,可从我的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文件操作**”获取~