C语言-文件操作2

fread和fwrite函数的用法
fscanf和fprintf函数的用法
rewind和fseek函数的用法

fgets函数有局限性,每次只能读一行数据,因为fgets函数遇到换行符就结束读取,如果希望读取多行数据,需要使用fread函数;相应的写入函数为fwrite函数。

对于Windows 系统,使用fread()和fwrite()时应该以二进制的形式打开文件,之所以这样,是因为:

  • Windows 平台中,程序中是 “\n”,对应的文件内容是 “\r\n”;而二进制文件的写入和读取按其原样。例:定义 fp 是 Windows 平台中以文本文件形式打开的文件指针,则 fprintf(fp, “\n”); 运行后,文件中多了 “\r\n”。

  • Mac 平台中,程序中是 “\n”,对应的文件内容是 “\n\r”;而二进制文件的写入和读取按其原样。例:定义 fp 是 Mac 平台中以文本文件形式打开的文件指针,则 fprintf(fp, “\n”); 运行后,文件中多了 “\n\r”。

  • Linux 平台中,文本文件和二进制文件没有任何区别。

fread函数用来从指定文件中读取块数据,所谓块数据,也就是若干个字节的数据,可以是字符,可以是字符串,可以是多行数据,并没有什么限制: fread()

size_t fread(void *ptr, size_t size, size_t count, FILE *fp);

fwrite()

size_t fwrite(void *ptr, size_t size, size_t count, FILE *fp);

参数说明:

  • ptr为内存区块的指针,它可以是数组、变量、结构体等。fread()中的ptr用来存放读取到的数据,fwrite()中ptr用来存放要写入的数据。
  • size:表示每个数据块的的字节数
  • count: 表示要读写的数据块块数
  • fp:表示文件指针
  • 理论上,每次读写size*count个字节的数据

size_t实在stdio.h和stdlib.h头文件中使用typedef定义的数据类型,表示无符号整数,也即非负数,常量用来表示数量。

返回值:返回成功读写的块数,也即count。如果返回值小于count:

  • 对于fwrite()来说,肯定发生了写入错误,可以用ferror()函数检测。
  • 对于fread()来说,可能读到了文件末尾,可能发生了错误,可以用ferror()或feof()检测。

数据写入完毕后,位置指针在文件的末尾,想要读取数据,必须将文件指针移动到文件开头,这就是rewind(fp);的作用。

文件后缀也可以不是.txt,它可以是任意的,你可以自己命名。

#include <stdio.h>
#define N 2

struct stu{
  char name[10];//姓名
  int num;//学号
  int age;//年龄
  float score;//成绩
}boya[N], boyb[N], *pa, *pb;

int main(){
  FILE *fp;
  int i;
  pa = boya;
  pb = boyb;
  if((fp = fopen("D:\\demo.txt", "wb+")) == NULL){
    puts("Fail to open file");
    exit(0);
  }
  puts("Input data:\n");
  for(i=0; i<N; i++,pa++){ //将数据填入结构体
    scanf( " %s, %d, %d, %f " , pa->name, &pa->num, &pa->age, &pa->score);
  }
  //将结构体类型数组boya中的数据存入文件中
  fwrite(boya, sizeof(struct stu), N, fp);
  //判断写入是否成功
  if(ferror(fp))
    puts("写入错误");
  else
    puts("写入成功");
  //将文件指针重置到文件开头
  rewind(fp);
  //将文件中的内容输出到结构体数组boyb中
  fread(pb, sizeof(struct stu), N, fp)
  //判断读取是否成功
  if(ferror(fp))
    puts("读取错误");
  else
    puts("读取成功");
  for(i = 0; i < N; i++, pb++){
    printf("%s, %d, %d, %f", pb->name, pb->num, pb->age, pb->score);
  }
  fclose(fp);
  return 0;
}

运行结果:

Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom  2  15  90.500000
Hua  1  14  99.000000

fscanf()函数和fprintf()函数的用法

fscanf()和fprintf()函数与前面使用的scanf和printf函数相似,都是格式化读写函数,两者的区别在于fscanf()和fprintf()目标不是显示器,而是文件。

int fprintf(FILE *fp, char *format, ...);
int fscanf(FILE *fp, char *format, ...);

fp为文件指针,format为格式控制字符串,……表示参数列表。与格式化输入输出并没有什么不同,只是多了一个文件指针fp。

  • fprintf()返回成功写入的字符个数,失败则返回负数。
  • fscanf()返回成功参数列表中被成功赋值的参数个数。
#include <stdio.h>
#define N 2

struct stu{
  char name[10];
  int num;
  int age;
  float score;
}boya[N], boyb[N], *pa, *pb;
int main(){
  FILE = *fp;
  int i;
  pa = boya;
  pb = boyb;
  if((fp = fopen("D:\\demo.txt", "w+")) == NULL){
    puts("Fail to open file!");
    exit(0);
  }
  puts("Input data:\n");
  //从键盘上输入数据输入到结构体类型数组boya中
  for(i=0; i<N; i++,pa++){
    scanf(" %s, %d, %d, %f" ,pa->name, &pa->num, &pa->age, &pa->score);
  }
  //把pa的指针重新指向boya的第一个字符
  pa = boya;
  //将boya中的数据输入到文件中
  for(i=0; i<N; i++,pa++){
    fprintf(fp, " %s, %d, %d, %f" ,pa->name, pa->num, pa->age, pa->score);
  } 
  //将文件中的数据取出放到结构体类型数组boyb中
  for(i=0; i<N; i++,pb++){
    fscanf(fp, " %s, %d, %d, %f" ,pb->name, &pb->num, &pb->age, &pb->score);
  }
  pb = boyb;
  //将结构体类型数组boyb中的数据输出到显示器上
  for(i=0; i<N; i++,pb++){
    printf(" %s, %d, %d, %f" ,pb->name, pb->num, pb->age, pb->score);
  }
  fclose(fp);
  return 0;
}

随机读写文件

我在前面介绍的文件读写函数都是顺序读写,即从头开始,依次读写各个数据。但实际开发中经常需要读写文件的中间部分,要解决这个问题,就得先移动文件内部的指针,再进行读写,这种读写方式称为随机读写,也就是说从文件任意位置开始读写。

实现随机读写的关键在于,移动文件内部的位置指针,这称为文件的定位。

文件定位函数rewind和fseek

移动文件内部位置指针的函数主要有两个:

  • rewind()用来将位置指针移动到文件开头,前面已经多次使用:
void rewind(FILE *fp);
  • fseek()用来将位置指针移动到任意位置:
int fseek( FILE *fp, long offset, int origin);

参数说明:

  1. fp为文件指针,也就是被移动的文件。
  2. offset为偏移量,也就是要移动的字节数。之所以是long类型,是希望移动的范围足够大,能处理的文件更大。offset为正时,向后移动;offset为负时,向前移动。
  3. origin为起始位置,也就是从何处开始计算偏移量。C语言规定起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:
起始点 常量名 常量值
文件开头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

fseek函数一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。

文件的随机读写

在移动位置指针之后,就可以用前面介绍的任何一种读写函数进行读写了。由于是二进制文件,因此常用fread()和fwrite()读写。

#include <stdio.h>
#define N 3

struct stu{
  char name[10];
  int num;
  int age;
  float score;
}boys[N], boy, *pboys;
int main(){
  FILE *fp;
  int i;
  pboys = boys;
  if((fp = fopen("D:\\demo.txt", "wb+")) == NULL){
    puts("Cannot open file, press any key to exit");
    getch()
    exit(1);
  }
  puts("Input data:");
  for(i=0; i<N; i++,pboys++){
    scanf(" %s %d %d %f", pboys->name, &pboys->num, &pboy->age, &pboy->score);
  }
  fwrite(boys, sizeof(struct stu), N, fp);
  fseek(fp, sizeof(struct stu), 0);
  fread(&boy,sizeof(struct stu), 1, fp);
  printf("%s %d %d %f", boy.name, boy.num, boy.age, boy.score);
  fclose(fp);
  return 0;
}

Posts in this Series