【C语言】第10章 对文件的输入输出

第10章 对文件的输入输出

文件的分类

程序文件、数据文件、磁盘文件、输入文件、输出文件、

“文件”指存储在外部介质上数据的集合

输入操作时,数据从文件流向计算机内存
输出操作时,数据从计算机流向文件

无论是用Word打开或保存文件,还是C程序中的输入输出都是通过操作系统进行的

“流”是一个传输通道,数据可以从运行环境流入程序中,或从程序流至运行环境

文件标识包括三部分:
(1) 文件路径
(2) 文件名主干
(3) 文件后缀

根据数据的组织形式,数据文件可分为ASCII文件和二进制文件。

数据在内存中是以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件
如果要求在外存上以ASCII代码形式存储,则需要在存储前进行转换
ASCII文件又称文本文件,每一个字节放一个字符的ASCII代码

字符一律以ASCII形式存储

【C语言】第10章 对文件的输入输出

文件缓冲区

【C语言】第10章 对文件的输入输出

打开和关闭文件

用fopen函数打开数据文件

用fclose函数关闭数据文件

fopen函数的调用方式为:
fopen(文件名,使用文件方式);
例如: 
fopen(“a1”,”r”); 
表示要打开名为“a1”的文件,使用文件方式为“读入”
fopen函数的返回值是指向a1文件的指针
    
通常将fopen函数的返回值赋给一个指向文件的指针变量。如:
FILE *fp; 
fp=fopen(“a1”,”r”);
fp和文件a1相联系,fp指向了a1文件
    
在打开一个文件时,通知编译系统以下3个信息:
① 需要访问的文件的名字
② 使用文件的方式(“读”还是“写”等)
③ 让哪一个指针变量指向被打开的文件

r---只读

w---只写

a---希望向文件末尾添加新的数据

r+、w+、a+、输入数据和输出数据

如果打开失败,fopen函数将带回一个空指针值NULL

常用下面的方法打开一个文件:
  if ((fp=fopen(“file1”,’r″))==NULL)
  {  printf(“cannot open this file\n”);
     exit(0);//终止正在执行的程序
  }

标准输入流、标准输出流、标准出错输出流。

关闭文件用fclose函数。fclose函数调用的一般形式为
fclose(文件指针); 
例如: 
fclose  (fp); 

顺序读写数据文件

读写一个字符的函数

函数名 调用形式 功能 返回值
fgetc fgetc(fp) 从fp指向的文件读入一个字符 读成功,带回所读的字符,失败则返回文件结束标志EOF(即-1)
fputc fputc(ch,fp) 把字符ch写到文件指针变量fp所指向的文件中 写成功,返回值就是输出的字符;输出失败,则返回EOF(即-1)
例10.1 从键盘输入一些字符,逐个把它们送到磁盘上去,直到用户输入一个“#”为止。
思路:用fgetc函数从键盘逐个输入字符,然后用fputc函数写到磁盘文件即可。

#include <stdio.h>
#include <stdlib.h>
int main()
{  FILE *fp;
   char ch, filename[10];
   printf("请输入所用的文件名:");
   scanf("%s",filename);
   if((fp=fopen(filename,"w"))==NULL)  
   {  printf("无法打开此文件\n");   
      exit(0); 
   }
   ch=getchar( );
   printf("请输入一个字符串(以#结束):");
   ch=getchar( );  
   while(ch!=‘#‘)   
   {  fputc(ch,fp);   
      putchar(ch);   
      ch=getchar(); 
    }
    fclose(fp);   
    putchar(10);  
    return 0;
}
例10.2 将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求将上例建立的file1.dat文件中的内容复制到另一个磁盘文件file2.dat中。
思路:处理此问题的算法是:从file1.dat文件中逐个读入字符,然后逐个输出到file2.dat中。

#include <stdio.h>
#include <stdlib.h>
int main( )
{  FILE *in,*out; 
   char ch, infile[10], outfile[10];   
   printf("输入读入文件的名字:");
   scanf("%s",infile);   
   printf("输入输出文件的名字:");
   scanf(“%s”,outfile);   
   if ((in=fopen(infile,“r”))==NULL)   
   {  printf("无法打开此文件\n"); exit(0);}
   if ((out=fopen(outfile,“w”))==NULL)   
   {  printf("无法打开此文件\n"); exit(0); }
   while(!feof(in))  //检查当前读写位置是否移到文件末尾 
   {   ch=fgetc(in);  
       fputc(ch,out);   
       putchar(ch);   
   }
   putchar(10);   
   fclose(in);   
   fclose(out);   
   return 0;
}

读写一个字符串的函数

函数名 调用形式 功能 返回值
fgetc fgetc(fp) 从fp指向的文件读入长度为(n-1)的字符串,存放到字符数组str中 读成功,返回地址str,失败则返回NULL)
fputc fputc(ch,fp) str所指向的字符串写到文件指针变量fp所指向的文件中 写成功,返回0;否则返回非0值

说明:
fgets函数的函数原型为:
char *fgets (char *str, int n, FILE *fp);
其作用是从文件读入一个字符串
调用时可以写成:
fgets(str,n,fp);

fgets(str,n,fp);中n是要求得到的字符个数,但实际上只读n-1个字符,然后在最后加一个’\0’字符,这样得到的字符串共有n个字符,把它们放到字符数组str中
如果在读完n-1个字符之前遇到换行符“\n”或文件结束符EOF,读入即结束,但将所遇到的换行符“\n”也作为一个字符读入
执行fgets成功,返回str数组首地址,如果一开始就遇到文件尾或读数据错,返回NULL

fputs函数的函数原型为:
int fputs (char *str, FILE *fp);
str指向的字符串输出到fp所指向的文件中
调用时可以写成: fputs(″China”,fp);
fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针
字符串末尾的′\0′不输出
输出成功,函数值为0;失败,函数值为EOF

例10.3 从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。
思路:为解决问题,可分为三个步骤:
从键盘读入n个字符串,存放在一个二维字符数组中,每一个一维数组存放一个字符串;
对字符数组中的n个字符串按字母顺序排序,排好序的字符串仍存放在字符数组中;
将字符数组中的字符串顺序输出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
int main()
{  FILE *fp;
   char  str[3][10],temp[10];   
   int i,j,k,n=3;
   printf("Enter strings:\n"); 
   for(i=0;i<n;i++) gets(str[i]);
   for(i=0;i<n-1;i++)   
   {  k=i;
      for(j=i+1;j<n;j++)
         if(strcmp(str[k],str[j])>0) k=j;
      if(k!=i)
      {  strcpy(temp,str[i]); strcpy(str[i],str[k]); 
         strcpy(str[k],temp);}
   }
   if((fp=fopen("string.dat", "w"))==NULL)   
   {  printf("can‘t open file!\n"); exit(0);}
   printf("\nThe new sequence:\n");
   for(i=0;i<n;i++)
   {  fputs(str[i],fp);
      fputs("\n",fp);  
      printf("%s\n",str[i]);   
   }
   return 0;
}

用格式化的方式读写文件

一般调用方式为:
fprintf(文件指针,格式字符串,输出表列);
fscanf (文件指针,格式字符串,输入表列);
如:
fprintf (fp,”%d,%6.2f”,i,f);
fscanf (fp,”%d,%f”,&i,&f);

用二进制方式向文件读写一组数据

fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);

例10.4 从键盘输入10个学生的有关数据,然后把它们转存到磁盘文件上去。
思路:定义有10个元素的结构体数组,用来存放10个学生的数据
从main函数输入10个学生的数据
用save函数实现向磁盘输出学生数据
用fwrite函数一次输出一个学生的数据

#include <stdio.h>
#define SIZE 10
struct Student_type
{  char name[10];
   int num;
   int age;
   char addr[15];
 }stud[SIZE]; 

void save( )   
{  FILE *fp;   int i;
   if ((fp=fopen("stu.dat","wb"))==NULL)      
   {  printf("cannot open file\n");
       return;
   }
   for (i=0;i<SIZE;i++)
      if (fwrite(&stud[i], sizeof(struct Student_type), 1,fp)!=1)
         printf("file write error\n");
   fclose(fp);
}

int main()
{  int i;
   printf("enter data of students:\n");
   for(i=0;i<SIZE;i++)   
      scanf("%s%d%d%s",
         stud[i].name,&stud[i].num,
         &stud[i].age,stud[i].addr);
   save( );
   return 0;
}
为了验证在磁盘文件“stu.dat”中是否已存在此数据,可以用以下程序从“stu.dat”文件中读入数据,然后在屏幕上输出。
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
struct Student_type
{  char name[10];
   int num;
   int age;
   char addr[15];
}stud[SIZE]; 

int main( )
{  int i;   FILE *fp;
   if ((fp=fopen("stu.dat","rb"))==NULL)
   {  printf("cannot open file\n"); exit(0); }
   for (i=0;i<SIZE;i++)
   {  fread (&stud[i],sizeof(struct
	Student_type),1,fp);   
       printf (“%-10s %4d %4d  %-15s\n”,
 	stud[i].name,stud[i].num,
 	stud[i]. age,stud[i].addr);
   }
   fclose (fp);  
   return 0;
}
如果修改例10.4:从已有的二进制文件“stu.list”中,读入数据并输出到“stu.dat”文件中,应如何修改程序? 
思路:编写load函数
main函数中再调用load函数

void load( )
{  FILE *fp;   int i;   if((fp=fopen("stu_list","rb"))==NULL)   
   {  printf("cannot open infile\n"); return;}
   for (i=0;i<SIZE;i++)
      if (fread(&stud[i],sizeof(struct student_type),1,fp)!=1)   
      {  if(feof(fp)) 
         {  fclose(fp);   return;}
         printf("file read error\n");
      }
   fclose (fp);
}
int main()
{  load();
 save(); 
   return 0;
}
随机读写数据文件

文件位置标记的定位
可以强制使文件位置标记指向指定的位置
可以用以下函数实现:
(1) 用rewind函数使文件标记指向文件开头
rewind函数的作用是使文件标记重新返回文件的开头,此函数没有返回值。

例10.5 有一个磁盘文件,内有一些信息。要求第一次将它的内容显示在屏幕上,第二次把它复制到另一文件上。
思路:因为在第一次读入完文件内容后,文件标记已指到文件的末尾,如果再接着读数据,就遇到文件结束标志,feof函数的值等于1(真),无法再读数据
必须在程序中用rewind函数使位置指针返回文件的开头

#include<stdio.h>
int main()
{  FILE *fp1,*fp2;
   fp1=fopen(“file1.dat”,“r”);   
   fp2=fopen(“file2.dat”,“w”);   
   while(!feof(fp1))   
       putchar(getc(fp1));   
   putchar(10);  
   rewind(fp1);   
   while(!feof(fp1)) 
       putc(getc(fp1),fp2);   
   fclose(fp1);   fclose(fp2);
   return 0;
}

文件位置标记的定位
可以强制使文件标记指向指定的位置
可以用以下函数实现:
(2) 用fseek函数改变文件标记
fseek函数的调用形式为:
fseek(文件类型指针,位移量,起始点)
起始点0代表“文件开始位置”,1为“当前位置”,2为“文件末尾位置”

C标准指定的名字

起始点 名 字 用数字代表
文件开始位置 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾位置 SEEK_END 2

fseek函数一般用于二进制文件。下面是fseek函数调用的几个例子:
fseek (fp,100L,0);
fseek (fp,50L,1);
fseek (fp,-10L,2);

文件位置标记的定位
可以强制使文件位置标记指向指定的位置
可以用以下函数实现:
(3) 用ftell函数测定文件位置标记的当前位置
ftell函数的作用是得到流式文件中文件位置标记的当前位置。

由于文件中的文件位置标记经常移动,人们往往不容易知道其当前位置,所以常用ftell函数得到当前位置,用相对于文件开头的位移量来表示。如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。例如:
i=ftell(fp);
if(i==-1L) printf(“error\n”);

例10.6 在磁盘文件上存有10个学生的数据。要求将第1,3,5,7,9个学生数据输入计算机,并在屏幕上显示出来。(要求:从例10.4中建立的“stu.dat”中读入数据)
思路:按二进制只读方式打开文件
将文件位置标记指向文件的开头,读入一个学生的信息,并把它显示在屏幕上
再将文件标记指向文件中第3,5,7,9个学生的数据区的开头,读入相应学生的信息,并把它显示在屏幕上
关闭文件

#include<stdio.h>
#include <stdlib.h>
struct St
{  char name[10];
   int num;
   int age;
   char addr[15];
}stud[10]; 
int main()
{  int i;  FILE *fp;         
   if((fp=fopen(“stu.dat”,“rb”))==NULL)  
   {  printf("can not open file\n"); exit(0); }
   for(i=0;i<10;i+=2)
   {  fseek(fp,i*sizeof(struct St),0);  
      fread(&stud[i], sizeof(struct St),1,fp);  
      printf(“%-10s %4d %4d %-15s\n”,
 	stud[i].name,stud[i].num,
 	stud[i].age,stud[i].addr);  
    }
    fclose(fp);  return 0;
}
文件读写的出错检测
  1. ferror函数
    ferror函数的一般调用形式为:ferror(fp);
    如果返回值为0,表示未出错,否则表示出错
    每次调用输入输出函数,都产生新的ferror函数值,因此调用输入输出函数后立即检查
    调用fopen时,ferror的初始值自动置为0

  2. clearerr函数
    作用是使文件错误标志和文件结束标志置为0
    调用一个输入输出函数时出现错误(ferror值为非零值),立即调用clearerr(fp),使ferror(fp)值变0,以便再进行下一次检测
    只要出现文件读写错误标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数

【C语言】第10章 对文件的输入输出

上一篇:Leetcode - 34. 在排序数组中查找元素的第一个和最后一个位置


下一篇:【Python机器学习实战】决策树与集成学习(三)——集成学习(1)