读写模式

FILE 结构体

三种刷新缓冲区的操作

简单读写示例: fgetc 与 fputc

fgets 与 fputs

fread 与 fwrite

fscanf 与 fprintf

rewind 和 fseek 随机读写文件

#include <stdio.h>
fclose() 的返回值为0,如果返回非零值则表示有错误发生

  • 文本模式和二进制模式的区别
    • windows
      • windows中fopen函数用文本模式写入文件时,会自动将\n换成\r\n,实现自动换行。
      • 用二进制模式时,写入文件的是\n,windows并不能识别到换行,因此出现了小方块。
    • linux系统中两种模式就没有区别

读写模式

模式 介绍
“r”(只读) 为输入打开一个文本文件 对文件进行读操作
“w”(只写) 为输出打开一个文本文件 对文件进行写操作
“a”(追加) 向文本文件尾添加数据
“rb”(只读) 为输入打开有一个二进制文件
“wb”(只写) 为输出打开一个二进制文件对文件进行写操作
“ab”(追加) 向二进制文件尾添加数据
“r+”(读写) 为读写打开一个文本文件
“w+”(读写) 为读写建立一个新的文本文件
“a+”(读写) 向文本文件尾添加数据
“rb+”(读写) 为读写打开一个二进制文件
“wb+”(读写) 为读写建立一个新的二进制文件
“ab+”(读写) 为读写打开一个进制文件
"t" 文本文件,如果不写默认"t"
  • 调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。
  • 读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:
    • 将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
    • 将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+"

FILE 结构体

struct _iobuf {
        char *_ptr;   // 下一个要被读取的字符地址
        int   _cnt;    // 剩余的字符,如果是输入缓冲区,那就表示缓冲区中还有多少个字符未被读取
        char *_base;  // 缓冲区基地址
        int   _flag;  // 读写状态标志位
        int   _file;  // 文件描述符
        int   _charbuf;
        int   _bufsiz; // 缓冲区大小
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

三种刷新缓冲区的操作

  • fclose
  • fflush(fp)
  • 写满

简单读写示例

#include <iostream>
#include <error.h>

int main()
{
    FILE *fp;
    char c,ret;

    fp=fopen("1.txt","r+");
    if(NULL == fp) 
    {   
        perror("fopen");
        goto error;
    }   
    /*  
    // 操作成功返回 该字符,操作失败(已经读到了文件文件末尾) 返回 “EOF”
    while((c = fgetc(fp)) != EOF){
        putchar(c);
    }
    */
    c='H';
    // 运行成功返回该字符,运行失败,返回 EOF(-1)                                                                          
    ret=fputc(c,fp);
    if(EOF != ret)
    {   
        perror("fputc");
        goto error;
    }   
    fclose(fp);
error:
    return 0;
}

fgets 与 fputs

只能读字符串,不能读取整型数据,或者结构体类型的

  • 函数原型 int fputs(const char *s, FILE *stream);

    • 写入成功返回非负数,失败返回 EOF
  • 函数原型 char *fgets(char *s, int n, FILE *stream)

    • 读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL
    • 如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL
    • 读取到的字符串会在末尾自动添加 '\0'
fgets() 遇到换行时,会将换行符一并读取到当前字符串.
gets() 不一样,它会忽略换行符.

fread 与 fwrite

  • 函数原型 size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
  • 函数原型 size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
    • ptr 为内存区块的指针,它可以是数组、变量、结构体等。
      • fread() 中的 ptr 用来存放读取到的数据
      • fwrite() 中的 ptr 用来存放要写入的数据。
    • size:表示每个数据块的字节数。
    • count:表示要读写的数据块的块数。
    • fp:表示文件指针。
      • 理论上,每次读写 size*count 个字节的数据。
      • 对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
      • 对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
示例
 #define  N 2
  struct stu{
      char name[10];
      int num;
      int age;
      float score;
  }boya_t[N],boyb_t[N],*pa,*pb;
  void work4()
  {
      FILE *fp;
      pa = boya_t;
      pb = boyb_t;
      int i;
      if((fp=fopen("1.txt","wb+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      printf("Input data:\n");
      for (i=0;i<N;++i,pa++)
      {   
          scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }   
      fwrite(boya_t,sizeof(struct stu),N,fp);
      rewind(fp);
      fread(boyb_t,sizeof(struct stu),N,fp);
      for (i=0;i<N;++i,pb++)
      {   
       printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);   
      }   
      fclose(fp);
  
  err:
      return;
  }
  void work5()
  {
      FILE *fp;
      int i;
      pb = boyb_t;
      if((fp=fopen("1.txt","rb+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      fread(boyb_t,sizeof(struct stu),N,fp);
      for (i=0;i<N;++i,pb++)
      {   
       printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);   
      }   
      fclose(fp);
  err:
      return;
  }
  
  int main()
  {
      work5();                                                                                                             
      return 0;
  }
  

fscanf 与 fprintf

  • fscanf() 和 fprintf() 和前面使用的 scanf() 和 printf() ,功能相似都是格式化读写函数
    • 区别fscanf 和 fprintf 的读写对象不是键盘和显示器, 而是磁盘文件
  • 函数原型 int fscanf ( FILE *fp, char * format, ... );
  • 函数原型 int fprintf ( FILE *fp, char * format, ... );
    • fprintf() 返回成功写入的字符的个数,失败则返回负数.fscanf() 返回参数列表中被成功赋值的参数个数。
示例
  void work4()
  {
      FILE *fp;
      int i;
      pa = boya_t;
      pb = boyb_t;
      if((fp = fopen("1.txt","wt+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      for (i=0;i<N;++i,pa++)
      {   
          scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }   
      pa = boya_t;
      for (i=0;i<N;++i,pa++)
      {   
          // 将boya中的数据写入到文件
       fprintf(fp,"%s %d %d %f\n",pa->name,pa->num,pa->age,pa->score);
      }   
      // 重置文件指针
      rewind(fp);
      // 从文件中读取数据,保存到boyb中
      for (i=0;i<N;++i,pb++)
      {   
          fscanf(fp,"%s %d %d %f\n",pb->name,&pb->num,&pb->age,&pb->score);
      }   
      pb=boyb_t;
      for (i=0;i<N;++i,pb++)
      {   
          printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);
      }   
      fclose(fp);
  err:
      return;
  }

rewind 和 fseek 随机读写文件

实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。常用函数有两个 rewind() 和 fseek()。

  • 函数原型 void rewind ( FILE *fp );
  • 函数原型 int fseek ( FILE *fp, long offset, int origin );
    • fp 为文件指针,也就是被移动的文件。
    • offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。
      • offset 为正时,向后移动;offset 为负时,向前移动。
    • origin 为起始位置,也就是从何处开始计算偏移量。
    • fseek() 一般用于二进制文件
      起始点 常量名 常量值
      文件开头 SEEK_SET 0
      当前位置 SEEK_CUR 1
      文件末尾 SEEK_END 2
示例
  void work6()
  {
      FILE *fp;
      int i;
      pa = boya_t;
      if((fp = fopen("1.txt","wb+")) == NULL)
      {
          perror("fopen");
          goto err;
      }
      printf("Input data:\n");
      for (i=0;i<N;++i,pa++)
      {
       scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }
      fwrite(boya_t,sizeof(struct stu),N,fp);
      fseek(fp,sizeof(struct stu),SEEK_SET);
      fread(&boyb_t,sizeof(struct stu),1,fp);
      printf("%s %d %d %5.2f\n",boyb_t.name,boyb_t.num,boyb_t.age,boyb_t.score);
      fclose(fp);
  err:
      return;
  }