C语言-对文件的输入输出

C语言中的文件类型

主要是两种文件类型:1. 文本文件(ASCII),2.二进制文件
一般来说,人类可读的文件都作为文本文件,例如一片文章。其余都作为二进制文件,例如一个程序。

ASCII码文件每一个字节存放一个ASCII代码,代表一个字符,因此便于对字符进行逐个处理,也便于输出字符。但是一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。
二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。用二进制输出数值,可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。一般中间结果数据需要暂时保存在外存上以后又需要输入到内存的,常用二进制文件保存

文件缓冲区

缓冲区文件系统:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。
在这里插入图片描述
非缓冲区文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。

文件类型指针

在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。

定义说明文件指针的一般形式为:

FILE *指针变量标识符;
其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。在编写源程序时不必关心FILE结构的细节。
在使用文件时,需要在内存中为其分配空间,用来存放文件的基本信息,给结构体类型是由系统定义的,C语言规定该类型为FILE型,其声明如下:

typedef  struct
{     short  level;                         // 缓冲区“满”或“空”的程度  
      unsigned  flags;                  // 文件状态标志  
      char  fd;                              // 文件描述符   
      unsigned  char  hold;        //  如无缓冲区不读取字符 
      short   bsize;                       // 缓冲区的大小 
      unsigned  char  * buffer;   // 数据缓冲区的位置 
      unsigned  char  *curp;      // 指针,当前的指向 
      unsigned       istemp;          // 临时文件,指示器  
      short  token;                      // 用于有效性检查 
}  FILE;

例如:FILE *fp;

表示fp是指向FILE结构的指针变量,通过fp即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。

文件的打开与关闭(fopen,fclose)

下面是这个函数调用的原型:

FILE *fopen( const char * filename, const char * mode );

由于文件的打开与关闭都是利用系统函数来实现的,因此,在编写有关文件的程序时,应该在其中包含“stdio.h”头文件。

文件的打开(fopen 函数)

FILE * fp;

fp=  fopen (文件名, 使用文件方式)

例如: fp=fopen ( "a1", "r");

文件名:参数为字符指针类型,实参为带路径的字符串。
使用文件方式:参见表下表。
“r”、“w”、“a”、 “r+”、“w+”、“a+”
“rb”、“wb”、“ab”、 ……

说明: “r”、“w”、"a"等打开文件的方式。
打开文件的常规方式:

if ( ( fp=fopen ("file1","r"))==NULL)
{  printf("Cannot open this file \n");
   exit(0);
}

文件中的回车换行符<=>内存中的换行符。

使用文件方式
文本文件文件使用方式含义如果指定文件不存在
"r" (只读)为输入打开一个文本文件出错
"w" (只写)为输出打开一个文本文件建立新的文件
"r+" (读写)为读/写打开一个文本文件出错
"w+" (读写) 为读/写建立一个新的文本文件建立新的文件
"a" (追加)向文本文件尾增加数据出错
"a+" (读写) 为读/写打开一个文本文件出错
二进制文件"rb" (只读)为输入打开一个二进制文件出错
"wb” (只写)为输出打开一个二进制文件建立新的文件
"rb+" (读写) 为读/写打开一个二进制文件出错
"wb+" (读写)为读/写建立一个新的二进制文件建立新的文件
"ab" (追加)向二进制文件尾增加数据出错
"ab+" (读写)为读/写打开一个二进制文件出错

在这里插入图片描述
对上表做以下补充说明:

1.  程序中凡是用“r”打开一个文件时,表明该文件必须已经存在,且只能从该文件读出数据。

2.用“w”打开的文件也只能向该文件写入数据。若打开的文件不存在,则按照指定的文件名建立该文件,若打开的文件已经存在,则将该文件删除,重建一个新文件。使用时要特别注意这一点。

3.  如果要向一个已经存在的文件后面追加新的信息,那只能用“a”方式打开文件。但此时该文件必须是存在的,否则将会出错。 

在打开一个文件之前,应该定义文件型指针,以便接收函数fopen返回的地址。如果出错,fopen将返回一个空指针NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并做相应的处理。
例如:

if((fp=fopen(“file1”,”rb”))= =NULL)
            {
                printf(“\n error on open file1”);
                getch( );
                exit(1);
             }

该程序段表示:如果返回的指针为空,则不能打开当前目录下的文件“file1”,同时给出错误提示信息“error on open file1”。程序中的getch()函数的功能是从键盘输入一个字符,该字符不在屏幕上显示。其实getch()在这里的作用是停留等待,只有当用户从键盘敲任意键时,程序才继续执行,我们可以利用这个等待时间来阅读出错提示,找到错误原因。当敲任意键后,执行语句“exit(1);”,从而退出程序。

文件的关闭

文件一旦使用完毕,应使用关闭文件函数fclose把文件关闭,以避免文件数据丢失等情况的发生。

fclose函数调用的一般形式为:

 fclose(FILE *fp);

其中,参数fp是文件型指针,通过fopen()函数已经获得,它指向某个打开的文件。例如:

fclose(fp);

上述语句的含义是关闭fp所指向的文件,同时自动释放分配给文件的内存缓冲区。当正常完成关闭文件的操作时,fclose函数的返回值为0,表示已正确关闭指定的文件;如返回非0值则表示有错误发生。

【例】文件的打开与关闭应用举例。

#include <stdio.h>
 
int main()
{
   FILE *fp = NULL;
 
   fp = fopen("/tmp/test.txt", "w+");
   fprintf(fp, "This is testing for fprintf...\n");
   fputs("This is testing for fputs...\n", fp);
   fclose(fp);
}

文件的读写(fputc,fgetc, fread,fwrite函数)

文件的读写操作由文件读写函数完成,常用的读写函数有fputc、fgetc、fread、fwrite、fputs、fgets、fprintf、fscanf、putw、getw等。

函数名调用方式功能返回值
fputcfputc( ch, fp)把字符写入文件指针变量 fp指向的文件输出成功,返回被写入的字符如果发生错误,则返回 EOF,并设置错误标识符
fgetcfgetc(fp)从fp指向的文件读入一个字符读成功,带回所读的字符,失败则返回文件结束的EOF(即-1)

说明: fgetc的第一个字符f代表文件(file),中间的get表示获取,最后一个c表示字符。

字符读写函数

读字符函数——fgetc函数

#include <stdio.h>

int main ()
{
   FILE *fp;
   int c;
   int n = 0;
  
   fp = fopen("file.txt","r");
   if(fp == NULL) 
   {
      perror("打开文件时发生错误");
      return(-1);
   }
   do
   {
      c = fgetc(fp);
      if( feof(fp) )
      {
          break ;
      }
      printf("%c", c);
   }while(1);

   fclose(fp);
   return(0);
}

写字符函数——fputc函数

#include <stdio.h>
 
int main ()
{
   FILE *fp;
   int ch;
 
   fp = fopen("file.txt", "w+");
   for( ch = 33 ; ch <= 100; ch++ )
   {
      fputc(ch, fp);
   }
   fclose(fp);
 
   return(0);
}
#include <stdio.h>
 
int main ()
{
   FILE *fp;
   int c;
 
   fp = fopen("file.txt","r");
   while(1)
   {
      c = fgetc(fp);
      if( feof(fp) )
      {
          break ;
      }
      printf("%c", c);
   }
   fclose(fp);
   return(0);
}

例10.1 从键盘输入一些字符,逐个把他们送到磁盘上去,直到用户输入一个“#”为止。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE * fp;
	char ch, filename[10];
	printf("请输入所用的文件名:\n");
	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从键盘输入一些字符,逐个把他们送到磁盘上去,直到用户输入一个“#”为止。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* in, * out;
	char ch, infile[50], outfile[50];	//文件字符数组要大于文件名称所占字符数
	printf("enter the name of the read in file:\n");
	scanf("%s", infile);
	printf("enter the name of the out in file:\n");
	scanf("%s", outfile);
	if ((in = fopen(infile, "r")) == NULL)
	{
		printf("error\n");
		exit(0);
	}
	if ((out = fopen(outfile, "w")) == NULL)
	{
		printf("error\n");
		exit(0);
	}
	while (!feof(in))
	{
		ch = fgetc(in);
		fputc(ch, out);
		putchar(ch);
	}
	putchar(10);
	fclose(in);
	fclose(out);
	return 0;
}

【例10.3】从键盘输入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。

#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("D:\\CC\\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;
}

在VS2019下,需将源文件的gets、strcpy、fopen做些修改:

#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_s(str[i], 10);

	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_s(temp, strlen(str[i]) + 1, str[i]);
			strcpy_s(str[i], strlen(str[k]) + 1, str[k]);
			strcpy_s(str[k], strlen(temp) + 1, temp);
		}
	}
	fopen_s(&fp, "D:\\CC\\string.datt", "w");
	if (fp == 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;
}

例10.3读回字符串:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	FILE *fp;
	char str[3][10];
	int i=0;
	if((fp=fopen("D:\\CC\\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;
}

在VS2019下,需将源文件的fopen做些修改:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	FILE* fp;
	char str[3][10];
	int i = 0;
	fopen_s(&fp,"D:\\CC\\string.dat", "r");
	if (fp== 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;
}

字符串读写函数

字符串读写函数处理的文件类型主要是文本文件,分为读字符串函数和写字符串函数

写字符串函数——fputs

fputs函数的功能是向指定的文件写入一个字符串,其调用形式为:

fputs(字符串,文件指针);

字符串可以是字符串常量,也可以是字符数组名或指针变量,例如:

fputs(“Human”,fp);

上述语句的含义是把字符串“Human”写入fp所指的文件之中。

读字符串函数——fgets

fgets函数的功能是从指定的文件中读一个字符串到字符数组中,其调用形式为:

fgets (str,n,fp);

函数中的参数str是字符数组名;n是一个正整数,表示从文件中读出的字符串不超过n-1个字符。在向字符数组读入的最后一个字符后加上字符串结束标志’\0’。如果在读n-1个字符之前遇到换行符或EOF,读入工作结束。例如:

fgets (ch,50,fp);
 上述语句的含义是从fp所指的文件中读出49个字符送入字符数组ch中。

【例10.3】从键盘输入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。

#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("D:\\CC\\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;
}

在VS2019下,需要源文件的gets、strcpy、fopen做些修改:

#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_s(str[i], 10);

	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_s(temp, strlen(str[i]) + 1, str[i]);
			strcpy_s(str[i], strlen(str[k]) + 1, str[k]);
			strcpy_s(str[k], strlen(temp) + 1, temp);
		}
	}
	fopen_s(&fp, "D:\\CC\\string.dat","w", "w");
	if (fp == 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;
}

例10.3读回字符串:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	FILE *fp;
	char str[3][10];
	int i=0;
	if((fp=fopen("D:\\CC\\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;
}

在VS2019下,需将源文件的fopen做些修改:

fopen_s(&fp,("D:\\CC\\string.dat", "r");

格式化读写函数

格式化写函数fprintf

格式化写函数fprintf的调用格式为:

fprintf(文件指针,格式字符串,输出列表);

例如:

fprintf(fp, "%d%c",j,ch);

上述语句的作用是将整型变量j和字符型变量ch的值按%d和%c的格式输出到fp所指的文件上。

格式化读函数fscanf

格式化读函数fscanf的调用格式为:

fscanf (文件指针,格式字符串,输入列表);

例如:

fscanf (fp, “%d%s”,&i,str);

若此时fp所指的文件中存放着以下的数据:
6happynewyear
上述语句的作用是将6赋给整型变量i,将 “happynewyear” 赋给字符型数组str。

例10.4 从键盘输入10个学生的有关数据,然后把它们转存到磁盘文件上去。

#include <stdio.h>
#define SIZE 3
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("cant 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 date of studnet:\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;
}

读入数据,输出程序如下:

#include <stdio.h>
#include <stdlib.h>
#define SIZE 3
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("cannt 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;
}

数据读写函数

写数据函数——fwrite函数

写数据块函数调用的一般形式为:

fwrite(buffer,size,n,fp); 

fwrite函数中各个参数的含义及说明

参数含义说明
buffer是一个字符型指针,它表示存放输出数据的变量地址或数组首地址
size是一个无符号整型,表示数据块的字节数
n无符号整型,表示要读写的数据块块数每个数据是size个字节
n表示文件指针

读数据函数——fread函数

读数据块函数调用的一般形式为:

  fread(buffer,size,n,fp);

其中的buffer是一个字符型指针,表示存放读入数据的变量地址或数组首地址。size、n、fp的含义见上表。例如:

fread(a,4,8,fp);

上述语句的含义是从fp所指的文件中,每次读4个字节,也就是把一个实数送入实数数组a中,连续读8次,即读入8个实数并送到数组a中。

文件的定位

文件头定位函数-rewind

当读取了文件中若干个数据后,又要从头读取数据,这时就需要将文件内部指针重新指向文件头,C语言提供的文件头定位函数rewind就可以将文件指针重新指定到文件头。该函数的调用格式为:
rewind(文件指针);
其功能是把文件内部的位置指针移到文件开头,如果定位成功,返回0;否则,返回非0。

文件随机定位函数- fseek

 前面学习的文件读写函数主要是用来读写顺序文件的。例如:如果要读取文件中的第6个数据,则打开文件后必须先读取前5个数据后,在读取1个数据才是所需要的数据。而文件随机定位函数可以使文件内部位置指针直接指向第6个数据,在读取当前一个数据就可以。
 fseek函数用来移动文件内部位置指针,其调用形式为:
fseek(文件指针,位移量,起始点);

“文件指针”指向被移动的文件。

“位移量”表示移动的字节数,要求位移量是 long型数据,以便在文件长度大于64KB时不会出错。当用常量表示位移量时,要求加后缀“L”。

“起始点”表示从何处开始计算位移量,C语言规定的起始点有三种:文件首、当前位置和文件尾,表示方法可以用下表来说明

起始点的表示方法

起始点表示符号数字表示
文件首SEEK_SET0
当前位置SEEK_CUR1
文件末尾SEEK_END2

流式文件的定位函数

用ftell()函数可以返回文件指针的当前位置。其调用格式为:

ftell(fp);

该函数的返回值为长整型数,表示相对于文件头的字节数,出错时返回-1L。
例如:

 long i;
      if((i=ftell(fp)) ==-1L)
      printf(" A file error has occurred at
      %ld.\n",i);

上述程序段可以通知用户出现了文件错误。

判断文件结束函数feof

程序从一个磁盘文件中逐个读取字符并输出到屏幕上显示,在while循环中常以EOF作为文件结束标志。这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~255,不可能出现-1,因此可以用EOF作为文件结束标志。

当把数据以二进制形式存放到文件中时,就会有-1值的出现,此时不能采用EOF作为二进制文件的结束标志。为解决这一问题,ANSIC提供了feof函数,用来判断文件是否结束。如果遇到文件结束,函数feof(fp)的值为1,否则为0。feof函数既可用以判断二进制文件是否结束,也可用以判断文本文件是否结束。

文件的出错检测

C标准提供了ferror和clearerr等函数用来检测输入/输出函数调用中的错误。

ferror函数

在调用各种输入/输出函数时,除了函数本身的返回值可以判断调用是否出错外,还可以用ferror函数进行测试。

ferror函数的调用格式为

 ferror(fp);
  1. 其中fp是指向文件的指针,当ferror函数的返回值为非0时,表示出错;当返回值为0时,表示没有发生错误。

  2. 需要读者注意的是,ferror函数对于同一个文件,在每次调用输入/输出函数时,均产生一个新的ferror函数值。因此最好是每次调用输入/输出函数时立即检查ferror函数值,以防止信息的丢失。

  3. 此外,在执行fopen函数时,ferror函数的初始值自动置0。

clearerr函数

clearerr函数的功能是使文件错误标志和结束标志置0。其调用格式为:

   clearerr(fp);
 其中的fp为文件指针。

在调用一个输入/输出函数出错时,ferror函数返回了非零值。例如:

clearerr(fp);
 在执行上述语句后,ferror(fp)的返回值变成了0。 

小结

由于文件的打开与关闭都是利用系统函数来实现的,因此,在编写有关文件的程序中,应该在其中包含stdio.h头文件

文件打开函数fopen用来打开一个文件,文件一旦使用完毕,应使用关闭文件函数fclose把文件关闭,以避免文件数据丢失等情况的发生。

fgetc函数用来从指定的文件读入一个字符,fputc函数用来将一个字符写入指定的文件中;写数据函数fwrite,读数据函fread;写字符串函数fputs,读字符串函数fgets;格式化写函数fprintf,格式化读函数fscanf

文件头定位函数rewind,文件随机定位函数fseek,流式文件的定位函数ftell,判断文件结束函数feof

C标准提供了ferror和clearerr等函数用来检测输入/输出函数调用中的错误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值