利用文件保存数据

C文件的有关概念

什么是文件

  • 文件(file)有不同的类型,在进行C语言程序设计中,主要用到两种文件:
  • 程序文件。包括源程序文件(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件是用来存放程序的,以便实现程序的功能。
  • 数据文件。文件的内容不是程序,而是供程序运行时读写的数据,如在程序运行过程中输出到磁盘(或其他外部设备)的数据,或供程序运行时读入内存的数据。如一批学生的成绩数据,或货物交易的数据等。
  • 程序所处理的数据的输入和输出,都是以终端为对象的,即从终端键盘输入数据,运行结果输出到终端上。实际上,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘(或光盘)上保存起来,以后需要时再从磁盘(或光盘)中输入到计算机内存。这就要用到磁盘(或光盘)文件。
  • 为了简化用户对输入输出设备的操作,使用户不必去区分各种输入输出设备之间的区别,操作系统把各种设备都统一作为文件来处理。从操作系统的角度看,每一个与主机相联的输入输出设备都看作是一个文件。例如,终端键盘是输入文件,显示屏和打印机是输出文件。
  • 所谓"文件"一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的数据,然后再从该文件中读取数据。
  • 输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为(stream),即输入输出流。流表示了信息从目的端的流动。在输入操作时,数据从文件流向计算机内存,在输出操作时,数据从计算机流向文件(如打印机、磁盘文件、光盘文件)。
  • C语言把文件看作是一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成。一个输入输出流就是一个字节流或二进制流。在C文件中,数据由一连串的字符(字节)组成,中间没有分隔符,对文件的存取是以字符(字节)为单位的,可以从文件读取一个字符或向文件输出一个字符。输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制这就增加了处理的灵性。这种文件称为流式文件

文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。文件标识包括三部分:

  1. 文件路径
  2. 文件名主干
  3. 文件后缀
  • 文件名主干的命名规则遵循标识符的命名规则。后缀用来表示文件的性质,一般不超过3个字母,如:
    .doc(Word生成的文件),.txt(文本文件),.dat(数据文件),.c(C语言源程序文件),.cpp(C++源程序文件)等

文件的分类

  • 根据文件中数据的组织形式,可分为ASCII文件二进制文件。ASCII文件又称文本(text)文件,它的每一个字节放一个字符的ASCII代码。二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上,也可以用二进制形式存储。如有短整型数,其值为10000,如果用ASCII码形式输出到磁盘,则在磁盘中占5个字节(每一个字符占一个字节),而用二进制形式输出,则在磁盘上只占2个字节。
  • 用ASCII码形式输出与字符一一对应,一个字节代表一个字符,因而便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。一般作为中间结果的数值型数据,需要暂时保存在外存上,义后又需要输入到内存的,常使用二进制文件保存。

文件缓冲区

  • C语言采用"缓冲文件系统"处理文件,所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送给磁盘去。如果从磁盘向内存读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)。

文件指针

  • 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE
  • 定义FILE结构体类型的信息包含在头文件"stdio.h"中。在程序中可以直接用FILE类型名定义变量。每一个FILE类型变量对应一个文件的信息区,其中包含该文件的有关信息。
  • 例如,可以定义以下FILE类型的变量:FILE f;
    以上定义了一个结构体变量f,可以用它来存放一个文件的有关信息。这些信息是建立文件时根据文件的性质由编译系统自动放入的,用户不必过问。
  • 一般不对FILE类型的变量命名,也就是不通过变量的名字来引用这些变量,而是设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。
  • 下面定义一个指向文件型数据的指针变量FILE *fp;。定义fp为一个指向FILE类型变量的指针变量。可以使fp指向某一个文件的文件信息区(是一个结构体变量),通过该文件信息区中的信息能够访问该文件。也就是说,通过文件指针变量能够找到与它相关的文件。

文件的打开与关闭

  • 对文件读写之前应该"打开"该文件,在使用结束之后应"关闭"该文件
  • 所谓"打开"是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据);所谓"关闭"是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了。

用fopen函数打开数据文件

  • 用标准输入输出函数fopen来实现打开文件
  • fopen函数的调用方式通常为:fopen(文件名,使用文件方式);
    例如:fopen("a1","r");
    表示要打开名字为"a1"的文件,使用文件的方式为"读入"(r代表read,即读入)。fopen函数的返回值是指向a1文件的指针(即a1文件信息区的起始地址)。通常将fopen函数的返回值赋给一个指向文件的指针变量。如:
FILE * fp; //定义一个指向文件的指针变量fp
fp=open("a1","r"); //将fopen函数的返回值赋给指针变量fp

这样fp就和文件a1相联系了,或者说,fp指向了a1文件。可以看出,在打开一个文件时,通知编译系统以下3个信息:

  1. 要打开的文件名,也就是准备访问的文件的名字
  2. 使用文件的方式("读"还是"写"等)
  3. 让哪一个指针变量指向被打开的文件
文件使用方式含义如果指定的文件不存在
“r”(只读)为输入打开一个已存在的ASCII文件出错
“w”(只写)为输出打开一个ASCII文件建立新文件
“a”(追加)向ASCII文件尾添加数据出错
“rb”(只读)为输入打开一个二进制文件出错
“wb”(只写)为输出打开一个二进制文件建立新文件
“ab”(追加)向二进制文件尾添加数据出错
“r+”(读写)为读写打开一个ASCII文件出错
“w+”(读写)为读写建立一个新的ASCII文件建立新文件
“a+”(读写)为读写打开一个ASCII文件出错
“rb+”(读写)为读写打开一个二进制文件出错
“wb+”(读写)为读写建立一个新的二进制文件建立新文件
“ab+”(读写)为读写打开一个二进制文件出错
  • 表中最基本的是"r"、“w”、“a"三种方式。在其后加"b"表示是二进制文件,不加"b"的代表是ASCII文件(即文本文件)。加”+"表示既可读又可写。
  • 如果不能实现"打开"的任务,fopen函数将会带回一个出错信息。出错的原因可能是用"r"方式打开一个并不存在的文件;磁盘出故障;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针NULL(NULL在stdio.h文件中已被定义为0)。
if ((fp = fopen("file1", "r")) == NULL)
{
	printf("cannot open this file\n");
	exit(0);
}

编译系统先检查打开的操作是否出错,如果有错就在终端上输出"cannot open this file"。exit函数的作用是关闭所有文件,终止正在执行的程序,待用户检查出错误,修改后再运行。

用fclose函数关闭文件

  • 在使用完一个文件后应该关闭它,以防止它再被误用。“关闭"就是撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,也就是文件指针变量与文件"脱钩”,此后不能再通过该指针对原来与其相联系的文件进行读写操作,除非再次打开,使该指针变量重新指向该文件。
  • 关闭文件用fclose函数。fclose函数调用的一般形式为:fclose(文件指针);。如:fclose(fp);
    前面曾把打开文件(用fopen函数)时所带回的指针赋给了fp,现在把fp指向的文件关闭,此后fp不再指向该文件。
  • 应该养成在程序终止之前关闭所有文件的习惯,如果不关闭文件将会丢失数据。因为,如前所述,在向文件写数据时,是先将数据输出到缓冲区,待缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区而程序结束运行,就会将缓冲区中的数据丢失。用fclose函数关闭文件,可以避免这个问题,它先把缓冲区中的数据输出到磁盘文件,然后才释放文件指针变量。
  • fclose函数也带回一个值,当顺利地执行了关闭操作,则返回值为0;否则返回EOF(-1)。

文件的顺序读写

  • 文件打开之后,就可以对它进行读写了。在顺序写时,先写入的数据存放在文件中前面的位置,后写入的数据存放在文件中后面的位置。在顺序读时,先读文件中前面的数据,后读文件中后面的数据。也就是说,对顺序读写来说,对文件读写数据的顺序和数据在文件中的物理顺序是一致的。

向文件读写字符

  • 对文本文件读入或输出一个字符的函数
函数名调用形式功能返回值
fgetcfgetc(fp)从fp指向的文件读入一个字符读成功,带回所读的字符,失败则返回文件结束标志EOF(即-1)
fputcfputc(ch,fp)把字符ch写到文件指针变量fp所指向的文件中输出成功,返回值就是输出的字符;输出失败,则返回EOF(即-1)

例:从键盘输入一些字符,逐个把它们送到磁盘上去,直到用户输入一个"#"为止

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

程序分析:

  • 用来存储数据的文件名可以在fopen函数中直接写成字符串常量形式(如指定"a1"),也可以在程序运行时由用户临时指定。本程序采取的方法是由键盘输入文件名。设立一个字符数组filename。用来存放文件名。运行时,从键盘输入磁盘文件名"filel. dat",操作系统就新建立一个磁盘文件filel.c,用来接收程序输出的数据。
  • 用fopen函数打开一个"只写"的文件(“w"表示只能写入不能从中读数据),如果打开成功,函数的返回值是该文件所建立的信息区的起始地址,把它赋给指针变量fp(fp已定义为指向文件的指针变量)。如果不能成功地打开文件,则在显示器的屏幕上显示"无法打开此文件”,然后exit终止程序运行。
  • exit是标准C的库函数,作用是使程序终止,用此函数时在程序的开头应加入stdlib.h头文
  • 用getchar函数接收用户从键盘输入的字符。注意每次只能接收一个字符。

例:将一个磁盘文件中的信息复制到另一个磁盘文件中。现要求将上例建立的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, "rb")) == NULL) //打开输入文件
	{
		printf("无法打开文件\n");
		exit(0);
	}
	if ((out = fopen(outfile, "wb")) == NULL) //打开输出文件
	{
		printf("无法打开此文件\n");
		exit(0);
	}
	while (!feof(in)) //如果未遇到输入文件的结束标志
	{
		ch = fgetc(in); //从输入文件读入一个字符,暂放在变量ch中
		fputc(ch, out); //将ch写到输出文件中
		putchar(ch); //将ch显示在屏幕上
	}
	putchar(10); //显示完全部字符后换行
	fclose(in); //关闭输入文件
	fclose(out); //关闭输出文件
	return 0;
}

程序分析:

  • 在访问磁盘文件时,是逐个字符(字节)进行的,为了知道当前访问到第几个字节,系统用"文件读写位置标记"来表示"当前所访问的位置"。开始时文件读写位置标记指向第1个字节,每访问完一个字节,当前文件读写位置标记就指向下一个字节,即当前读写位置自动后移。
  • 为了知道对文件的访问是否完成,只需看文件读写位置是否移到文件的末尾。用eof函数可以检查到文件读写位置标记是否移动文件的末尾,即磁盘文件是否结束。

向文件读写一个字符串

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

说明:

  1. 用fgets函数可以从指定的文件读入一个字符串。如:fgets(str,n,fp);。其中,n是要求得到的字符个数,但实际上只从fp所指向的文件中读入n-1个字符,然后在最后加一个’\0’字符,这样得到的字符串共有n个字符,把它们放到字符数组str中。如果在读完n-1个字符之前遇到换行符"\n"或文件结束符EOF,读入即结束,但将所遇到的换行符"\n"也作为一个字符读入。若执行fgets函数成功,则返回值为str数组首元素的地址,如果一开始就遇到文件尾或读数据出错,则返回NULL。
  2. 用fputs函数可以向指定的文件输出一个字符串。如:fputs("China",fp);。把字符串"China"输出到fp指向的文件中。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。字符串末尾的’\0’不输出。若输出成功,函数值为0;失败时,函数值为EOF。
  • fgets和fputs这两个函数的功能类似于gets和puts函数,只是gets和puts以终端为读写对象,而fgets和fputs函数以指定的文件作为读写对象。

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

#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]); //用strcmp函数对字符串比较大小
            strcpy(str[i], str[k]); //复合语句的作用是将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;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值