C语言进阶——文件操作(下)

简介: 随机读写函数,需要配合上面的输入输出函数使用,所谓的随机读写,是指通过改变文件指针的偏移量,来写入或读取数据。介绍三个和随机读取有关的函数

🌲随机读写

 随机读写函数,需要配合上面的输入输出函数使用,所谓的随机读写,是指通过改变文件指针的偏移量,来写入或读取数据。介绍三个和随机读取有关的函数:fseek 改变文件指针偏移量、ftell 查看当前文件指针的偏移量、rewind 使文件指针复原至起始位置。


🌱fseek

392ac02bc1ca4a6f972d4ebac5b53c75.png

//fseek,文件指针偏移量
int main()
{
  FILE* fp = fopen("test.txt", "w");
  if (NULL == fp)
  {
    perror("fopen::test.txt");
    return 1;
  }
  char* pc = "abc";
  fseek(fp, 20, SEEK_SET);//从起点往后偏移
  fputs(pc, fp);
  fclose(fp);
  fp = NULL;
  return 0;
}

4228fe1b25fd44ffb1d822d3ff353b8f.png

🌱ftell

98415911f6a64402b87eea2c050d5333.png

//ftell,返回当前文件指针偏移信息
int main()
{
  FILE* fp = fopen("test.txt", "r");
  if (NULL == fp)
  {
    perror(fp);
    return 1;
  }
  printf("当前文件指针偏移量为:%d\n", ftell(fp));
  fseek(fp, 20, SEEK_SET);//向后偏移20
  printf("经过fseek设置后的文件指针偏移量为:%d\n", ftell(fp));
  fclose(fp);
  fp = NULL;
  return 0;
}

3e7f5bdf38a64db8af718364dfe325ea.png


🌱rewind

214e3a480d274ab3ae2753e5e46f8608.png

//rewind,使文件指针恢复至原位置
int main()
{
  FILE* fp = fopen("test.txt", "r");
  if (NULL == fp)
  {
    perror("fopen::test.txt");
    return 1;
  }
  fseek(fp, 20, SEEK_SET);//先让文件指针向后偏移20
  printf("当前文件指针偏移量为:%d\n", ftell(fp));
  rewind(fp);//使文件指针恢复至起始位置
  printf("经过恢复后的文件指针偏移量为:%d\n", ftell(fp));
  return 0;
}

094de6266e5448829010da45bba5e958.png

🌱fseek、ftell、rewind 三合一

//fseek、ftell、rewind三合一
//假设文件中存储数据为abcdef
int main()
{
  FILE* fp = fopen("test.txt", "r");
  if (NULL == fp)
  {
    perror("fopen::test.txt");
    return 1;
  }
  printf("现在文件中内容为abcdef,我们要依次取出e、b、d\n");
  fseek(fp, -2, SEEK_END);//从后往前偏移
  printf("先取出字符%c\n", fgetc(fp));
  rewind(fp);//还原至起始位置
  fseek(fp, 1, SEEK_SET);//从前往后偏移
  printf("再取出字符%c\n", fgetc(fp));
  fseek(fp, 1, SEEK_CUR);//从当前位置向后偏移
  printf("最后再取出字符%c\n", fgetc(fp));
  fclose(fp);
  fp = NULL;
  return 0;
}

da3798d48acd4256b33ba2b9cdadef7c.png

注意:


每进行一次文件输入输出操作,文件指针都会向后移动一位。比如上面的三合一, 当我们读取到字符 'b' 后,文件指针向后移动一位,指向字符 'c' ,此时只需要把文件指针向后偏移一位,就能愉快的读取到字符 'd' 了。

🌲文本文件与二进制文件

🌱文本文件

 文本文件指以ASCII码(文本方式)存储的数据,原始数据机器能直接看懂,将内存中的数据对应ASCII码解码存储后,我们人类也能看懂,举个栗子,在记事本中写的文本,就是文本文件

4f5aeb6f241349449164407bef4e5e9d.png

🌱二进制文件

 二进制文件是将数据编译后转成二进制形式,然后直接存储的文件,这种文件机器能秒懂,读取效率很高(因为不需要转译),但二进制一般人是看不懂的,部分二进制数据也无法通过ASCII码解码为正确的数据,因此强行输出二进制文件,极有可能会得到乱码。比如将上面的那段话通过二进制形式写入文件中,可以看到除字符类型数外,其他类型的数据变成了乱码。

b47ca223bee94514b54b7c4adc13d1b8.png

 下图为上面的二进制文件在内存中以二进制形式存储的样子,显示为十六进制(节省空间),实际为二进制。

a1414be9d013406a8f6d38811237c080.png


🌱注意

如果待读取的文件中存储的是二进制数据,就需要使用 二进制读取 "rb" 的形式读取数据;反之如果想写入二进制数据,就需要用 二进制写入 "wb" ,无论是二进制还是普通文本,计算机都能读懂,只是我们看不得罢了。小技巧:可以使用二进制存储重要数据,这样外行人一时半会也理解不了。

🌲文件使用注意事项

🌱被错误使用的feof

 很多人在写C语言课设的时候(学生信息管理系统、通讯录系统等),会通过 feof 来判断文件是否读取结束,这是一种错误的用法,因为 feof 的作用是判断当前文件读取结束原因的,如果是因为读取到了末尾而结束,feof(fp) 就为真;除了这个以外,还有另一个文件读取结算原因判断函数,ferror ,当 ferror(fp) 为真时,说明此时发生了读取异常,并非正常结束,我们可以通过这两个报错函数来判断文件读取结束的真正原因。

f4147ce229904dff8b799346b859e044.png

  char arr[100] = "0";
  fgets(arr, sizeof(arr), fp);
  printf("%s\n", arr);
  int n = 0;
  if ((n = feof(fp)))
    printf("End by EOF\n");
  if (ferror(fp))
    printf("End by IO Error\n");

593962f13b2c46beab081b02af81f3f8.png

🌱文件读取结束原因判断

 既然 feof 不是用来判断读取是否结束的,那说明存在其他判断方法,其实答案就是函数设计中,前辈在设计函数时已经考虑好了,比如 fgetc 没有读取到数据会返回EOF,fgets 没有读取到数据会返回NULL,fscanf 可以通过其返回的实际读取元素个数进行判断,fread 可以通过返回值与指定读取的元素数比较。每种读取函数都有属于的自己的判断方法,比如下面这两个例子:


🪴对文本数据进行读取


//读取错误信息判断
//1.文本文件版,假设文件内已有信息,为abcdef
int main()
{
  FILE* fp = fopen("test.txt", "r");
  if (NULL == fp)
  {
    perror("fopen::test.txt");
    return 1;
  }
  int ch = 0;//接收读取的数据,要用整型,因为EOF为-1
  while ((ch = fgetc(fp)) != EOF)
  {
    printf("%c", ch);
  }
  //判断是为何结束
  if (feof(fp))
    printf("\nEnd by EOF(因读到文件末尾而结束)\n");
  else if (ferror(fp))
    printf("\nEnd by IO(因中途读取失败而结束)\n");
  fclose(fp);
  fp = NULL;
  return 0;
}

b8727d797e3941fc94efb8a28f446a73.png

🪴对二进制文件进行读取

//2.二进制文件版
enum { SIZE = 5 };//相当于宏定义
int main()
{
  double a[SIZE] = { 1.1,2.2,3.3,4.4,5.5 };
  //首先把五个浮点数以二进制的形式,写入文件中
  FILE* fp = fopen("testFoBin.txt", "wb");
  if (NULL == fp)
  {
    perror(fp);
    return 1;
  }
  fwrite(&a, sizeof(*a), SIZE, fp);
  fclose(fp);//写入完成,关闭文件
  double b[SIZE] = { 0.0 };
  //现在以二进制的形式读取数据
  fp = fopen("testFoBin.txt", "rb");
  int size = fread(&b, sizeof(*b), SIZE, fp);
  if (size == SIZE)
  {
    printf("No Error!\n");
    int i = 0;
    while (i < size)
      printf("%.2lf ", b[i++]);
  }
  else
  {
    if (feof(fp))
      printf("\nError by EOF\n");
    else if(ferror(fp))
      printf("\nError by IO\n");
  }
  fclose(fp);
  fp = NULL;
  return 0;
}

0148d87ed5a94cc9ad7a09b88f1095f7.png


🌱文件缓冲区

 ANSIC 标准定义了“缓冲文件系统”这个概念,所谓缓冲文件系统是指系统自动地在内存中为程序

中每一个正在使用的文件开辟一块“文件缓冲区”。无论是读取还是写入数据时,都会先将数据送入文件缓冲区,等文件缓冲区装满或遇到刷新指令后,数据才会被读取(写入)到目标空间中。文件缓冲区的大小是由编译器决定的。


af76c11477d04b00810ef185e836f8ac.png

🪴验证文件缓冲区是否存在


我们可以利用睡眠函数 Sleep 来使程序暂停,此时数据还没有被写入文件中,仍然位于缓冲区;之后再手动刷新缓冲区,数据此时会被推送至文件中。

//文件缓冲区
#include<windows.h>
int main()
{
  //打开文件
  FILE* fp = fopen("test.txt", "w");
  if (NULL == fp)
  {
    perror(fp);
    return 1;
  }
  char* ps = "测试文件缓冲区";
  fputs(ps, fp);//先将数据写到缓冲区中
  printf("数据现在已经在缓冲区里面了,但还没有推送到文件中\n");
  printf("程序睡眠10秒,10秒后刷新缓冲区\n");
  Sleep(10000);//睡眠函数,单位是毫秒
  fflush(fp);
  printf("现在缓冲区已经刷新,数据已经写入文件中了\n");
  Sleep(10000);
  //关闭文件,当文件关闭时,缓冲区也会被刷新
  fclose(fp);
  fp = NULL;
  return 0;
}


c5091e4d7019484b89a0e23bfc2b7158.png


5131b420b0a3497aa1c89c918017be48.png


4aa9a5bffa5341fc847ba2e1357e5a81.png

 可以看到文件缓冲区是真实存在的。


注意:


fclose 关闭文件后,会自动刷新缓冲区,数据能够推送至文件

当程序运行结束后,缓冲区也会被自动刷新

scanf 遇到 \n 也会触发缓冲区刷新,另外如果其在读取字符型数据时,遇到空白字符(空格、TAB键)也会触发缓冲区的刷新

🌳总结

 以上就是C语言文件操作的所有内容了,从文件的打开到文件的关闭,中间可以进行多种操作,构造出巧妙的数据。当然前提是我们得学会文件的相关操作,可以巧记为单字符读写、行读写、格式化读写和二进制读写,无论是那种操作,都需要和对应的文件操作指令匹配上;关于随机读写,记住那三个偏移量函数就行了;最后需要对文件缓冲区有一定的理解,确保数据能成功推送至文件内。总之,文件操作的学习可以宣布毕业了。

9b70c77871654752a443ff48e10cb29b.jpg


 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


目录
相关文章
|
22天前
|
存储 C语言
【c语言】玩转文件操作
本文介绍了C语言中文件操作的基础知识,包括文件的打开和关闭、文件的顺序读写、文件的随机读写以及文件读取结束的判定。详细讲解了`fopen`、`fclose`、`fseek`、`ftell`、`rewind`等函数的使用方法,并通过示例代码展示了如何进行文件的读写操作。最后,还介绍了如何判断文件读取结束的原因,帮助读者更好地理解和应用文件操作技术。
28 2
|
1月前
|
存储 C语言
C语言文件操作(2)
【10月更文挑战第2天】
|
1月前
|
程序员 编译器 C语言
C语言底层知识------文件操作
本文详细介绍了文件操作的基本概念,包括文件的分类(程序文件和数据文件,其中着重于数据文件的文本文件和二进制文件),流的概念及其在C程序中的应用,以及标准输入输出流stdin、stdout和stderr的作用。作者通过示例展示了如何使用fopen、fclose和常见的读写函数如fgetc、fputc和fgets进行文件操作。
22 2
|
2月前
|
C语言
C语言——文件操作
本文介绍了文件的基本操作,包括文件的打开、关闭、读取和写入。使用`fopen`函数以不同模式(如“r”、“w”等)打开文件,并通过`fclose`关闭。文章详细解释了如何利用`fputc`、`fputs`及`fprintf`进行格式化写入,同时介绍了`fgetc`、`fgets`和`fscanf`用于文件内容的读取。此外,还涵盖了二进制文件的读写方法以及如何通过`fseek`、`ftell`和`rewind`实现文件的随机访问。
51 1
C语言——文件操作
|
1月前
|
存储 缓存 编译器
文件操作——C语言
文件操作——C语言
|
1月前
|
存储 C语言
简述C语言文件操作
简述C语言文件操作
11 0
|
1月前
|
存储 文件存储 C语言
深入C语言:文件操作实现局外影响程序
深入C语言:文件操作实现局外影响程序
|
1月前
|
存储 程序员 编译器
C语言文件操作(1)
【10月更文挑战第1天】
|
1月前
|
存储 C语言
C语言的文件操作
C语言的文件操作
21 0
|
1月前
|
存储 移动开发 Unix
C 语言文件操作详解
C 语言文件操作详解