ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【C语言】—— 文件操纵(下) [打印本页]

作者: 悠扬随风    时间: 2024-8-29 08:36
标题: 【C语言】—— 文件操纵(下)
  
前言:

  
  
  在 【C语言】—— 文件操纵(上) 一文中,我们对文件有了一个简朴的了解,并学会了怎样打开和关闭文件,下面就让我们一起来学学怎样对文件进行读写吧。
  
  
五、文件的次序读写

5.1、 次序读写函数介绍

函数名功能实用于fgetc字符输入函数所有输入流fputc字符输出函数所有输出流fgets文本行输入函数所有输入流fputs文本行输出函数所有输出流fscanf格式化输入函数所有输入流fprintf格式化输出函数所有输出流fread二进制输入文件fwrite二进制输出文件   注:上面说的实用于所有输入流一般指实用于标准输入流和其他输入流(如文件输入流);所有输出流一般指实用于标准输出流和其他输出流(如文件输出流)
  下面我们对上述函数逐一进行介绍
  
5.2、                                   f                         p                         u                         t                         c                              fputc                  fputc 函数

   

  
函数使用:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         FILE* pf = NULL;
  5.         //打开文件
  6.         pf = fopen("test.txt", "w");
  7.         //文件操作
  8.         if (NULL == pf)
  9.         {
  10.                 perror("fopen fail");
  11.                 return 1;
  12.         }
  13.         //写文件
  14.         fputc('a', pf);
  15.         fputc('b', pf);
  16.         fputc('c', pf);
  17.         //写入26个字母
  18.         char ch = 0;
  19.         for (ch = 'a'; ch <= 'z'; ch++)
  20.         {
  21.                 fputc(ch, pf);
  22.         }
  23.         //关闭文件
  24.         fclose(pf);
  25.         pf = NULL;
  26. }
复制代码
  

    这样,字符就写好了。
  
  当写入字符时,还有一些细节需要注意:当一个文件打开时,最开始其实是有一个光标指向第一个位置,每当用                                    f                         p                         u                         t                         c                              fputc                  fputc 函数写入一个字符,光标则退却一格。光标是用来维护此时如今我们这个文件写到哪的,而且是按照一定的次序往后走的,因此叫做次序读写
  
5.3、                                    f                         g                         e                         t                         c                              fgetc                  fgetc 函数

   

  
函数使用:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         FILE* pf = NULL;
  5.         //打开文件
  6.         pf = fopen("test.txt", "r");
  7.         //文件操作
  8.         if (NULL == pf)
  9.         {
  10.                 perror("fopen fail");
  11.                 return 1;
  12.         }
  13.         //读文件
  14.         int ch = 0;
  15.         while ((ch = fgetc(pf)) != EOF)
  16.         {
  17.                 printf("%c ", ch);
  18.         }
  19.         printf("\n");
  20.         //关闭文件
  21.         fclose(pf);
  22.         pf = NULL;
  23. }
复制代码
运行结果:
   

    而同样,以只读的方式打开文件,刚开始光标是在第一个位置,即指向                                    a                              a                  a,每读一个字符,光标向退却一位。
  
5.4、                                    f                         p                         u                         t                         s                              fputs                  fputs 函数

   

  
   注:多次调用该函数,并不会实现自动换行,要想换行应自动输入‘\n’
  
函数使用:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         //打开文件
  5.         FILE* pf = NULL;
  6.         pf = fopen("test.txt", "w");
  7.         if (NULL == pf)
  8.         {
  9.                 perror("fopen fail");
  10.                 return 1;
  11.         }
  12.         //写文件
  13.         fputs("hello", pf);
  14.         fputs("world\n", pf);
  15.         fputs("hello csdn\n", pf);
  16.         //关闭文件
  17.         fclose(pf);
  18.         pf = NULL;
  19.         return 0;
  20. }
复制代码
运行结果:

  我们可以看到,加了换行符后,文件的光标是直接落到下一行的。
  
5.5、                                    f                         g                         e                         t                         s                              fgets                  fgets 函数

   

  
  
函数使用:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         //打开文件
  5.         FILE* pf = NULL;
  6.         pf = fopen("test.txt", "r");
  7.         if (NULL == pf)
  8.         {
  9.                 perror("fopen fail");
  10.                 return 1;
  11.         }
  12.         //读文件
  13.         char arr1[10] = "xxxxxxxxx";
  14.         fgets(arr1, 8, pf);
  15.         char arr2[10] = "xxxxxxxxx";
  16.         fgets(arr2, 8, pf);
  17.         //关闭文件
  18.         fclose(pf);
  19.         pf = NULL;
  20.         return 0;
  21. }
复制代码
运行结果:
   

    
5.6、                                    f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf 函数

   

    
  该函数的功能是将数据以格式化的形式写入流中(以文本的形式)
  
  其实,                                        f                            p                            r                            i                            n                            t                            f                                  fprintf                     fprintf 函数和                                         p                            r                            i                            n                            t                            f                                  printf                     printf 函数是非常相像的,让我们来对比一下
  
   

    
  他们的区别仅仅是第一个参数的有无而已,其他都是千篇一律的,所以你会用                                    p                         r                         i                         n                         t                         f                              printf                  printf你就会用                                    f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf
  
  多的一个参数是什么呢?是文件流,你需要将数据输出到的那个文件流
  
  1. #include<stdio.h>
  2. struct S
  3. {
  4.         char name[20];
  5.         int age;
  6.         float score;
  7. };
  8. int main()
  9. {
  10.         struct S s = { "张三", 20, 75.5f };
  11.         //打开文件
  12.         FILE* pf = NULL;
  13.         pf = fopen("test.txt", "w");
  14.         if (NULL == pf)
  15.         {
  16.                 perror("fopen fail");
  17.                 return 1;
  18.         }
  19.         //写文件
  20.         fprintf(pf, "%s %d %f", s.name, s.age, s.score);
  21.         //关闭文件
  22.         fclose(pf);
  23.         pf = NULL;
  24.         return 0;
  25. }
复制代码
运行结果:

  
5.7、                                    f                         s                         c                         a                         n                         f                              fscanf                  fscanf 函数

   

    
  该函数的功能是从文件流中读取格式化的数据。
  
  不难发现,                                        f                            s                            a                            n                            f                                  fsanf                     fsanf 与                                         s                            c                            a                            n                            f                                  scanf                     scanf 函数很像,我们来对比一下
  
   

    
  同                                    f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf 一样,                                   f                         s                         c                         a                         n                         f                              fscanf                  fscanf 与                                    s                         c                         a                         n                         f                              scanf                  scanf 只是相差一个参数而已,你会用                                    s                         c                         a                         n                         f                              scanf                  scanf 自然也就会用                                    f                         s                         c                         a                         n                         f                              fscanf                  fscanf 函数,第一个参数即是你所要读取的文件流。
  
  1. #include<stdio.h>
  2. struct S
  3. {
  4.         char name[20];
  5.         int age;
  6.         float score;
  7. };
  8. int main()
  9. {
  10.         struct S s = { 0 };
  11.         //打开文件
  12.         FILE* pf = NULL;
  13.         pf = fopen("test.txt", "r");
  14.         if (NULL == pf)
  15.         {
  16.                 perror("fopen fail");
  17.                 return 1;
  18.         }
  19.         //读文件
  20.         fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
  21.         //printf("%s %d %.2f\n", s.name, s.age, s.score);
  22.         fprintf(stdout, "%s %d %.2f\n", s.name, s.age, s.score);
  23.        
  24.         //关闭文件
  25.         fclose(pf);
  26.         pf = NULL;
  27.         return 0;
  28. }
复制代码
  
运行结果:
   

    注意看,上述代码用了                                         f                            p                            r                            i                            n                            t                            f                                  fprintf                     fprintf 来将数据打印在屏幕上
  还记得最开始的表格中,                                   f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf 最后一列写的是所有输出流吗?这所有输出流就包括了文件流和标准输出流,既然                                    f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf 可以输出到文件中,那么自然也就可以输出到屏幕中,完成                                    p                         r                         i                         n                         t                         f                              printf                  printf 一样的功能。
  而同理,前面讲的                                    f                         p                         u                         t                         c                              fputc                  fputc、                                   f                         g                         e                         t                         s                              fgets                  fgets、                                   f                         s                         c                         a                         n                         f                              fscanf                  fscanf 等函数也可以从标准输入(输出)流中获取(输出)数据。
  
5.8、                                    p                         r                         i                         n                         t                         f                         /                         f                         p                         r                         i                         n                         t                         f                         /                         s                         p                         r                         i                         n                         t                         f                              printf/fprintf/sprintf                  printf/fprintf/sprintf 函数对比

  
通过我们前面的学习,我们已经知道了                                    p                         r                         i                         n                         t                         f                              printf                  printf 和                                    f                         p                         r                         i                         n                         t                         f                              fprintf                  fprintf 函数的作用:

那么                                    s                         p                         r                         i                         n                         t                         f                              sprintf                  sprintf 函数又是作什么的呢?我们一起来看看
   

    
  该函数的作用是:将数据以格式化的形式写到字符串上。其实就是把格式化的数据转换成字符串
  
  1. #include<stdio.h>
  2. struct S
  3. {
  4.         char name[20];
  5.         int age;
  6.         float score;
  7. };
  8. int main()
  9. {
  10.         struct S s = { "张三", 20, 75.5f };
  11.         char buf[50] = { 0 };
  12.         sprintf(buf, "%s %d %f", s.name, s.age, s.score);
  13.         printf("%s\n", buf);
  14.         return 0;
  15. }
复制代码
运行结果:

  该代码完全是以 %s 的形式打印的,说明数据已经完全转换成字符串了。
  
5.9、                                    s                         c                         a                         n                         f                         /                         f                         s                         c                         a                         n                         f                         /                         s                         s                         c                         a                         n                         f                              scanf/fscanf/sscanf                  scanf/fscanf/sscanf 函数对比

  同样,通过我们前面的学习,我们已经知道了                                    s                         c                         a                         n                         f                              scanf                  scanf 和                                    f                         s                         c                         a                         n                         f                              fscanf                  fscanf 函数的作用:

  那                                    s                         s                         c                         a                         n                         f                              sscanf                  sscanf 的功能又是什么呢?学习了                                    s                         p                         r                         i                         n                         t                         f                              sprintf                  sprintf ,我们猜测,其应该是从字符串中读取格式化数据,是不是呢?我们一起来看看
  
   

    
函数功能:从字符串中读取格式化数据
  
  1. #include<stdio.h>
  2. struct S
  3. {
  4.         char name[20];
  5.         int age;
  6.         float score;
  7. };
  8. int main()
  9. {
  10.         struct S s = { "张三", 20, 75.5f };
  11.         char buf[50] = { 0 };
  12.         sprintf(buf, "%s %d %f", s.name, s.age, s.score);
  13.        
  14.         struct S a = { 0 };
  15.         sscanf(buf, "%s %d %f", s.name, &(s.age), &(s.score));
  16.         printf("%s %d %f\n", s.name, s.age, s.score);
  17.         return 0;
  18. }
复制代码
运行结果:
   

    
  
5.10、                                    f                         w                         r                         i                         t                         e                              fwrite                  fwrite 函数

   

  
  
下面我们直接上代码:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         int arr[] = { 1,2,3,4,5 };
  5.         int sz = sizeof(arr) / sizeof(arr[0]);
  6.         FILE* pf = NULL;
  7.         pf = fopen("test.txt", "wb");
  8.         if (NULL == pf)
  9.         {
  10.                 perror("fopen fail");
  11.                 return 1;
  12.         }
  13.         fwrite(arr, sizeof(arr[0]), sz, pf);
  14.         fclose(pf);
  15.         pf = NULL;
  16.         return 0;
  17. }
复制代码
  
我们以二进制的方式打开:
   

    
5.11、                                    f                         r                         e                         a                         d                              fread                  fread 函数

   

    该函数的作用是:以二进制的形式读取数据到内存中
  我们可以看到,这函数的参数与                                    f                         w                         r                         i                         t                         e                              fwrite                  fwrite 是大同小异的,这里就不逐一介绍了,我们直接上代码
  1. #include<stdio.H>
  2. int main()
  3. {
  4.         int arr[5] = { 0 };
  5.         FILE* pf = NULL;
  6.         pf = fopen("test.txt", "rb");
  7.         if (NULL == pf)
  8.         {
  9.                 perror("fopen fail");
  10.                 return 1;
  11.         }
  12.         fread(arr, sizeof(arr[0]), 5, pf);
  13.         for (int i = 0; i < 5; i++)
  14.         {
  15.                 printf("%d ", arr[i]);
  16.         }
  17.         printf("\n");
  18.         fclose(pf);
  19.         pf = NULL;
  20.         return 0;
  21. }
复制代码
运行结果:
   

    但是,上面代码是我提前知道了统共的数据个数,当我不知道数据具体个数是又该怎么办呢?
  这里,我们需要知道                                    f                         r                         e                         a                         d                              fread                  fread 函数的返回值,该函数的返回值是读取到的数据的个数。这时,当我要求读 7 个数据,而返回值是 5 时,说明数据读完了。
  上面的代码我们可以做如下修改:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         int arr[5] = { 0 };
  5.         FILE* pf = NULL;
  6.         pf = fopen("test.txt", "rb");
  7.         if (NULL == pf)
  8.         {
  9.                 perror("fopen fail");
  10.                 return 1;
  11.         }
  12.         int i = 0;
  13.         while (fread(arr + i, sizeof(arr[0]), 1, pf))
  14.         {
  15.                 printf("%d ", arr[i]);
  16.                 i++;
  17.         }
  18.         printf("\n");
  19.         fclose(pf);
  20.         pf = NULL;
  21.         return 0;
  22. }
复制代码
  
  
六、文件的随机读写

  前面我们所学习到的函数都是次序读写,光标是依次往后移动。那能不能做到随机读写呢,即我想在哪里读写就在哪读写,指那打那。
  当然是可以的,下面让我们一起来学习。
  
6.1、                                    f                         s                         e                         e                         k                              fseek                  fseek 函数

   

  
起始位置选择:
常量所指位置SEEK_SET文件的起始位置SEEK_CUR当前光标位置SEEK_END文件末端   
  这个函数有什么用呢?好比文件中有                                    a                         b                         c                         d                         e                         f                         g                              abcdefg                  abcdefg 的数据,当前光标指向                                    a                              a                  a,而我想直接读                                    e                              e                  e,这时就可以用该函数移动光标啦。
  
   

  例子:
  1. #inclu<stdio.h>
  2. int main()
  3. {
  4.         FILE* pf = NULL;
  5.         pf = fopen("test.txt", "r");
  6.         if (NULL == pf)
  7.         {
  8.                 perror("fopen fail");
  9.                 return 1;
  10.         }
  11.         char ch = 0;
  12.         ch = fgetc(pf);
  13.         printf("%c\n", ch);
  14.         fseek(pf, 3, SEEK_CUR);
  15.         ch = fgetc(pf);
  16.         printf("%c\n", ch);
  17.         fclose(pf);
  18.         pf = NULL;
  19.         return 0;
  20. }
复制代码
运行结果:
   

    
  
6.2、                                    f                         t                         e                         l                         l                              ftell                  ftell 函数

   

                                     f                         t                         e                         l                         l                              ftell                  ftell 函数会返回文件指针(光标) 相对于文件起始位置的 偏移量
  这里,我们想:假如我们让光标读到文件末尾,在返回偏移量,是不是就能知道文件的长度呢?答案是肯定的。
  
例子:
  1. #includ<stdio.h>
  2. int main()
  3. {
  4.         FILE* pf;
  5.         long size;
  6.         pf = fopen("test.txt", "rb");
  7.         if (NULL == pf)
  8.                 perror("Error opening file");
  9.        
  10.         fseek(pf, 0, SEEK_END); // non-portable
  11.         size = ftell(pf);
  12.         fclose(pf);
  13.         printf("Size of test.txt: %ld bytes.\n", size);
  14.        
  15.         return 0;
  16. }
复制代码
  
  
6.3、                                    r                         e                         w                         i                         n                         d                              rewind                  rewind 函数

   

                                       r                         e                         w                         i                         n                         d                              rewind                  rewind 函数可以让文件指针回到起始位置
  走的太远,别忘了回头路
例子:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.         int n;
  5.         FILE* pf;
  6.         char buffer[27];
  7.         pf = fopen("test.txt", "w+");
  8.         for (n = 'A'; n <= 'Z'; n++)
  9.         {
  10.                 fputc(n, pf);
  11.         }
  12.         rewind(pf);
  13.         fread(buffer, 1, 26, pf);
  14.         fclose(pf);
  15.         buffer[26] = '\0';
  16.         printf(buffer);
  17.         return 0;
  18. }
复制代码
  
  
七、文件读取结束的判断

7.1、 被错误使用的                                    f                         e                         o                         f                              feof                  feof

  
  很多人都以为                                    f                         e                         o                         f                              feof                  feof函数是用来直接判断文件读取是否结束。其实这是大错特错的
                                     f                         e                         o                         f                              feof                  feof 的作用是:当文件读取结束时,判断读取结束的原因是否是因为:遇到文件末尾结束。
     如今假设文件读取结束了,但是是什么原因读取结束的呢?
                                         f                         e                         o                         f                              feof                  feof 函数是判断是否是因为遇到文件末尾而结束的。
  而还有个函数叫                                         f                            e                            r                            r                            o                            r                                  ferror                     ferror 是用来判断是否是因为遇到错误而读取结束的
  
     其实在我们打开一个流时,会有两个标记值
      当读文件的过程中确实是遇到文件末尾了,就会将第一个值标记;遇到错误就会将第二个值标记。
                                          f                            e                            o                            f                                  feof                     feof是用来检测第一个标记的;                                        f                            e                            r                            r                            o                            r                                  ferror                     ferror是用来检测第二个标记的
                                     f                         e                         o                         f                              feof                  feof 函数:当文件确实是因为读取到文件末尾而结束时,返回一个非零值,反之返回 0
  
  
7.2、怎样判断文件读取结束

  那么怎样来判断文件是否读取结束呢?其实在前面结束各个函数时已经顺便介绍了:通过函数的返回值进行判断!
  
(1)文本文件判断

函数名正常读取返回值读取结束或遇到错误的返回值fgetc返回读取到的字符的ASCII码值EOFfgets返回目的空间的地点NULL   
(2)二进制文件判断

  二进制文件用                                    f                         r                         e                         a                         d                              fread                  fread 进行读取,                                   f                         r                         e                         a                         d                              fread                  fread 返回值是其读取到的个数。当其返回值小于实际要读取的个数时,表现文件读取结束
  
八、 文件缓冲区

  我们想一个问题:当我们想往文件中存 26 个字母,这 26 个字母是直接从程序(内存)中存到文件(硬盘)中的吗?
  其实不是的。
  ANSI C 标准采用 “缓冲文件体系” 处理的数据文件的,所谓缓冲文件体系指的是体系自动在内存中为程序中为每一个正在使用的文件开发一块“文件缓冲区”。
  从内存向磁盘输出数据会先送达内存中的缓冲区,装满缓冲区或自动刷新缓冲区才将数据送到磁盘上。
  假如从磁盘向盘算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区或刷新缓冲区),然后在从缓冲区逐个地将数据送到程序数据区(程序变量等)。
  缓冲区的大小根据C编译体系决定的。
   

    那为什么要有文件缓冲区呢?
  其实,当我们向文件中输入输出数据时,相关函数会调用操纵体系相关接口;这时假如写一个字符就调用一次操纵体系,是不是效率太低,并且我们的操纵体系上面可不止跑着一个程序,这时你一直打扰操纵体系,操纵体系就没法干活了。
  应用缓冲区,在缓冲区攒够一定数据再一次性全部录进,效率就会提升很多。
  下面,我们通过一段代码验证缓冲区的存在:
  1. #include<stdio.h>
  2. #include <windows.h>
  3. //VS2019 WIN11环境测试
  4. int main()
  5. {
  6.         FILE* pf = fopen("test.txt", "w");
  7.         fputs("abcdef", pf);//先将代码放在输出缓冲区
  8.         printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
  9.         Sleep(10000);
  10.         printf("刷新缓冲区\n");
  11.         fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
  12.         //注:fflush 在⾼版本的VS上不能使⽤了
  13.         printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
  14.         Sleep(10000);
  15.         fclose(pf);
  16.         //注:fclose在关闭⽂件的时候,也会刷新缓冲区
  17.         pf = NULL;
  18.         return 0;
  19. }
复制代码
  这里我们需要注意:
  因为有文件冲区的存在,C语言在操纵文件的时候,需要做刷新缓冲区大概在文件操纵结束的时候关闭文件。
  假如不做,可能导致读写文件的问题
  
  
  
  

  好啦,本期关于文件操纵的知识就介绍到这里啦,盼望本期博客能对你有所帮助。同时,假如有错误的地方请多多指正,让我们在C语言的学习路上一起进步!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4