C程序设计(谭浩强)随书代码验证-第十章 对文件的输入输出

C程序设计(谭浩强)随书代码验证-第十章 对文件的输入输出

源码

源码: github.

10.1C文件有关基本知识

主要用到两种文件:

  1. 程序文件
    包括源程序文件(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件的内容是程序代码
  2. 数据文件
    文件的内容不是程序,而是供程序运行时读写的数据,如在程序运行过程中输出到磁盘(或其他外部设备)的数据,或者在程序运行过程中供读入的数据

本章主要讨论数据文件

为了使用户避免区分不同设备的区别,操作系统把各种设备都统一作为文件来处理。

  • 文件名

    1. 文件路径
    2. 文件名主干
    3. 文件后缀
      在这里插入图片描述
  • 文件的分类

    1. 二进制文件
      数据在内存中是以二进制形式存储的,如果不加转换地输出到外存 ,就是二进制文件。可以认为他就是存储在内存数据的映像,称之为映像文件(image file).
    2. ASCII 文件
      如果要求在外存上以ASCII形式存储,则需要在存储前进行转换。 ASCII文件又称文本文件,每一个字节放一个字符的ASCII码
      在这里插入图片描述
  • 文件缓冲区

    ANSI C是美国国家标准协会(ANSI)对C语言发布的标准

    ANSI C 标准采用“缓冲文件系统”处理数据文件。
    缓冲文件系统:系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区
    在这里插入图片描述

  • 文件类型指针
    缓冲文件系统中,关键的概念是文件类型指针,简称文件指针

    每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件的状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。
    该结构体类型由系统声明,取名为FILE。
    由一种C编译环境提供的stdio.h头文件中有以下的文件类型声明:
    typedef struct
    {
    short level; //缓冲区“满”或“空”的程度
    unsigned flags; //文件状态标志
    char fd; //文件描述符
    unsigned char hold;//如缓冲区无内容不读取字符
    short bsize; //缓冲区的大小
    unsigned char * buffer; //数据缓冲区的位置
    unsigned char * curp; //指针当前的指向
    unsigned istemp;//临时文件指示器
    short token; //用于有效性检查
    }FILE;

不同的C编译系统的FILE类型包含的内容不完全相同,但大同小异。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10.2打开与关闭文件

  • 概述
    “打开”:为文件建立相应的信息区(用来存放有关文件的信息)和 文件缓冲区(用来暂存输入输出的数据)
    “关闭”:撤销文件信息区文件缓冲区

  • 用fopen函数打开数据文件
    ANSI C规定了标准输入输出函数fopen来实现打开文件

    • 调用方式
      fopen(文件名,使用方式);
      在这里插入图片描述
      打开名字为“a1”的文件,使用文件方式为“读入”.
      fopen函数的返回值是指向a1的指针(即 a1 文件信息区的起始地址)。
      使用举例:
      在这里插入图片描述

      • 打开一个文件时,通知编译器以下3个信息
        1. 需要打开文件的名字,即准备访问的文件的名字
        2. 使用文件的方式(读 写 等)
        3. 让哪一个指针变量指向被打开的文件

      文件使用方式见下表
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      从ASCII文件中读入计算机:回车换行符—>换行符
      从计算机输出至文本文件:换行符—>回车换行符

      二进制文件不存在这种转换
      在这里插入图片描述

  • 用fclose函数关闭数据文件
    撤销文件信息区 和 文件缓冲区

    • 调用形式
      fclose(文件指针);

    例:
    fclose(fp);

    • fclose ();函数调用的必要性:避免数据丢失
      在向文件中写入数据时,先将数据输出到缓冲区,,缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区,但是程序结束运行,就有可能使缓冲区数据丢失。
      如果调用fclose函数,关闭文件,它会先把数据缓冲区数据写到文件中,然后再撤销文件信息区。
      有的编译系统会在程序结束前自动先将数据缓冲区数据写到文件中,从而避免了这个问题。
      但是,建议养成在程序结束前关闭所有文件。
      fclose();成功返回0;否则返回EOF(-1) end of file

10.3顺序读写数据文件

  • 向文件读写字符
    在这里插入图片描述

10-1顺序读写数据文件例题

在这里插入图片描述

#include <stdlib.h>
#include <stdio.h>
int main()
{
	FILE *fp;
	char ch,filename[10];
	
	printf("请输入所用的文件名:");
	scanf("%s",filename);
	
	if((fp=fopen(filename,"w"))==NULL)       // 打开输出文件并使fp指向此文件 
	{
		printf("无法打开此文件\n");          // 如果打开时出错,就输出"打不开"的信息  
		exit(0);                             // 终止程序*/
	}
	
	ch=getchar( );                           // 接收在执行scanf语句时最后输入的回车符  
	printf("请输入一个准备存储到磁盘的字符串(以#结束):");
	ch=getchar( );                           // 接收从键盘输入的第一个字符 
	while(ch!='#')                           // 当输入'#'时结束循环  
	{
		fputc(ch,fp);                         // 向磁盘文件输出一个字符  
		putchar(ch);                          // 将输出的字符显示在屏幕上  
		ch=getchar();                         // 再接收从键盘输入的一个字符  	
	}
	fclose(fp);                              // 关闭文件  
	putchar(10);                             // 向屏幕输出一个换行符,换行符的ASCII代码为10 
	return 0;
}

在这里插入图片描述
在这里插入图片描述
程序分析
1. exit 是标准C的库函数,
作用:使程序终止,须包含stdlib.h头文件
2. getchar 函数 stdio.h
作用:接收用户从键盘输入的字符,每次只能接收一个字符。

10-2从一个文件 复制 到另一个文件中

在这里插入图片描述
在这里插入图片描述

#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);
	}
	
	ch = fgetc(in);                       //从输入文件读入一个字符,放在变量ch中
	
	while(!feof(in))                    // 如果未遇到输入文件的结束标志  
	{
		fputc(ch,out);                   // 将ch写到输出文件中  
		putchar(ch);                     // 将ch显示在屏幕上  
		ch = fgetc(in);                    // 从输入文件读入一个字符,放在变量ch中 
	}
	putchar(10);                        // 显示完全部字符后换行  
	fclose(in);                         // 关闭输入文件  
	fclose(out);                        // 关闭输出文件  
	return 0;
}

在这里插入图片描述
程序分析
1. 访问磁盘文件时,逐个字符(字节)进行的,系统用“文件读写位置标记”来表示当前所访问的位置。
2. feof函数
可以检查“文件读写位置标记”是否移动到文件末尾,即磁盘文件是否结束
feof(FILE * in) 如果结束,则为真(1);如果没有结束,则为假(0);
3. 以上程序是按照 文本 方式处理的。若改为二进制方式,将fopen函数中 “r” --> “rb”,“w” --> “wb”
4. C 系统已经将fput fgetc 函数定义为 宏putc getc
#define putc(ch,p) fputc(ch,p)
#define getc(ch,p) fgetc(ch,p)
在stdio.h中定义的,可以用作相同函数

向文件读写 字符串

背景:如果遇到需要读取多个字符,一个一个读取太麻烦,则引入** 字符串 **读取函数
获取函数:
在这里插入图片描述
- fgets函数原型
char * fgets(char * str, int n, FILE * fp);
其中,
n :要求得到的字符个数,实际获取字节数为 n-1 个字符,然后在最后加一个“\0”字符,共计 n 个字符
如果在读取 n-1 个字符之前遇到换行符“\n” 或 文件结束符EOF,读入结束,但将所遇到的换行符‘\n’,也作为一个字符读入。
若执行成功,则返回值为str数组首元素的地址,如果一开始就遇到文件尾或读数据出错,则返回NULL。
- fputs函数原型
int fputs(char * str , FILE * fp);
作用:将str所指向的字符串输出到fp所指向的文件中,调用时可写为 fputs(“China”, fp);
第一个函数变量可以是常量、字符数组名或字符型指针。字符串末尾“\0”,不输出。
若输出成功,函数值为0;失败时,函数值为EOF(-1)

fgets fputs 与 gets puts 对比
前者是以指定的文件作为读写对象,后者是以终端为读写对象

10-3-1字符串的保存

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
int main()
{  
	FILE *fp;
	char  str[3][10],temp[10];   // str是用来存放字符串的二维数组,temp是临时数组  
	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)     /* D:\\CC\\打开磁盘文件*/  
	{
		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;
}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10-3-2从文件中读取数据

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
int main()
{ 
	FILE *fp;
	char  str[3][10];
	int i=0;
	
	if((fp=fopen("D:\\string.dat","r"))==NULL)      // 注意文件名必须与前相同 
	{
		printf("can't open file!\n");
		exit(0);
	}
	while(fgets(str[i],10,fp)!=NULL)
	{
		printf("%s",str[i]);
		i++;
	}
	fclose(fp);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

用格式化的方式读写文件

上述fputc fputs fgetc fgets 数据对象为字符型数据,不满足其他数据类型的存取需求,故引入如下两个函数
- fprintf()
- fscanf()
调用方式:
fprintf(文件指针, 格式字符串, 输出列表);
fscanf(文件指针, 格式字符串, 输出列表);

fprintf(fp, “%d,%6.2f”, i, f); 比printf()多了个fp;
作用:将int 型变量 i 和 float型变量 f的值按照%d和%6.2f的格式控制输出到fp指向的文件中,若 i= 3 f = 4.5,则输出到磁盘文件上的是以下字符:
3, 4.50
这是和输出到屏幕的情况相似,只是它输出到的是文件,而不是屏幕

fscanf(fp,"%d, %f",&i, &j);
如果文件中有如下数据:3, 4.5 ,则从磁盘文件中读取的整数3送给i, 4.5 送给j

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

背景:ASCII方式:
向文件中存储数据时,需要对数据先进行ASCII转换,再存储
从文件中读取数据时,又需要将ASCII文件转换成二进制文件后在赋给内存变量
故此中花费了较多的时间在转换上,在需要频繁存取数据的时候,不建议使用fprintf fscanf函数
故:引入如下函数:fread fwrite

调用形式:
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
其中:
buffer:是一个地址。对于fread来说,它是用来存放从文件中读入的数据的存储区的地址
size:要读写的字节数
count:要读写多少个数据项(每个数据长度为size)
fp:FILE类型指针
示例
fread fwrite 函数的类型为int型,如果fread fwrite 函数执行成功,则函数返回值为形参count的值(即输入输出数据项的个数)

10-4-1 二进制数据文件的写入

在这里插入图片描述

#include <stdio.h>
#define SIZE 10
struct student_type
{
	char name[10];
	int num;
	int age;
	char addr[15];
}stud[SIZE];                // 定义全局结构体数组stud,包含10个学生数据  

void save( )                 // 定义函数save,向文件输出SIZE个学生的数据  
{
	FILE *fp;
	int i;
	if((fp=fopen ("stu.dat","wb"))==NULL)    // 打开输出文件atu_list  
	{
		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("Please enter data of students:\n");
	for(i=0;i<SIZE;i++)           // 输入SIZE个学生的数据,存放在数组stud中  
		scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr);
	save( );
	return 0;
}

在这里插入图片描述在这里插入图片描述

10-4-2 二进制数据文件的读取

#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)        // 打开输入文件atu.dat  
	{
		printf("cannot open file\n");
		exit(0) ;
	}
	for(i=0;i<SIZE;i++)
	{
		fread (&stud[i],sizeof(struct student_type),1,fp);       // 从fp指向的文件读入一组数据  
		printf ("%-10s %4d %4d  %-15s\n",stud[i].name,stud[i].num,stud[i]. age,stud[i].addr);
		// 在屏幕上输出这组数据 
	}
	fclose (fp);                                  // 关闭文件"stu.dat" 
	return 0;
}

在这里插入图片描述

向文件中存入数据:换行符 + 回车 = 换行符
从文件中读取数据:换行符 = 换行符 + 回车

10-4-3 从一个文件中读取数据 存向 另一个文件

在这里插入图片描述

#include <stdio.h>
#define SIZE 10
struct student_type
{
	char name[10];
	int num;
	int age;
	char addr[15];
}stud[SIZE];                // 定义全局结构体数组stud,包含10个学生数据  

void load( )
{
	FILE *fp;
	int i;
	if((fp=fopen("stu.dat","rb"))==NULL)     // 打开输入文件stu_list  
	{
		printf("cannot open infile\n");
		return;
	}
	for(i=0;i<SIZE;i++)
		if(fread(&stud[i],sizeof(struct student_type),1,fp)!=1)  // 从stu_ list文件中读数据  
		{
			if(feof(fp)) 
			{
				fclose(fp); 
				return;
			}
			printf("file read error\n");
		}
	fclose (fp);
}   

void save( )                // 定义函数save,向文件输出SIZE个学生的数据  
{
	FILE *fp;
	int i;
	if((fp=fopen ("stu_list","wb"))==NULL)    // 打开输出文件stu_list  
	{
		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()
{
	load();
	save( );
	return 0;
}

在这里插入图片描述
在这里插入图片描述

10.4随机读取数据文件

随机访问:
不是按照 数据在文件中的物理位置次序进行读写,而是可以对任何位置上的数据进行访问,这种方法比顺序访问效率高得多
文件位置标记及定位
文件读写位置标记–>简称 文件位置标记 (文件标记):指示 接下来要读写的下一字符的位置
(有的教材中称之为 文件位置指针、但是为了防止与指向文件的指针混淆,常称之为 文件位置标记

  • rewind函数
    作用:使文件为指标记重新返回文件的开头,此函数无返回值

10-5文件位置标记 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);   // 从文件头重新逐个读字符,输出到file2文件  
	fclose(fp1);
	fclose(fp2);
	return 0;
}

  • fseek函数
    • 函数形式:
      • fseek(文件类型指针, 位移量, 起始点)
        在这里插入图片描述
        位移量:
        位移量应是long型数据,在数字的末尾加上字母L,则表示long型
        在这里插入图片描述
  • ftell函数
    • 测定文件位置标记的当前位置
      得到流式文件中 文件位置标记 的当前位置
      返回值为:相对于文件开头的偏移量来表示。
      若:调用出错,(不存在fp指向的文件)返回值为-1L。
      在这里插入图片描述

10-6 随机读写 举例

在这里插入图片描述

#include <stdlib.h>
#include<stdio.h>
struct student_type       // 学生数据类型  
{ 
	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 student_type),0);        // 移动位置指针 
		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-5文件读写的出错检测

  • ferror函数
    在这里插入图片描述
    在这里插入图片描述
  • clearerr函数
    在这里插入图片描述

end

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值