如果想持续地保存数据,就必须借助外存设备,如磁盘。程序语言提供访问和使用外存的功能,外存信息存放在操作系统管理下的文件系统里,通过文件和目录的方式来组织的,文件是封装起来的一组数据,目录是子目录和文件的集合。目录或文件可以通过名字来使用。程序在执行中与外存打交道,主要就是访问和使用作为外存信息实体的各种文件。
程序输入输出的对象可以是各种设备,如键盘、显示器、打印机等,也可以是文件。许多操作系统采用统一的观点,把与输入输出有关的操作都统一到“文件”这一概念,把键盘、显示器等设备也看作文件,给定“文件名”,对它们的操作都通过“文件名”来进行。
c语言没有专用于输入输出的的语言结构,ANSI C把文件和输入输出功能作为标准库的一部分,以提高程序的可移植性,而且与输入输出有关的机制都统一到文件的概念。标准头文个件<stdio.h>描述了这方面的数据结构和函数。
什么是流?
文件是输入输出的对象,为与文件交换信息,需要建立与它们的联系,而流就是这种联系,这种建立联系(创建流)的动作被形象地称为打开文件。为从一个文件输入,程序需要创建与该文件关联的输入流,同理要向一个文件输出,就要建立与之关联的输出流。当然还可以建立既能输入又能输出的流。
标准的流分为两类:字符流和二进制流。所谓字符流把文件看作字符行的序列,每行包含0个或多个字符,最后有换行符’\n’。而二进制流把数据按内存里的形式直接存入文件,二进制流操作保证将数据写入文件后再以同样方式读回,数据的形式和内容都不改变,由于操作不做数据形式转换,在保存和装入大数据时有速度优势,但这种保存形式不适合人的阅读。
流是如何实现的呢?
流通过一种特殊数据结构实现,标准库为此定义了类型FILE
以下是MinGw在库stdio.h中对FILE的结构描述
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
以下是VC在库corecrt_internal_stdio.h中对FILE的结构描述
struct __crt_stdio_stream_data
{
union
{
FILE _public_file;
char* _ptr;
};
char* _base;
int _cnt;
long _flags;
long _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
CRITICAL_SECTION _lock;
};
这种类型的对象用于保存与流有关的所有信息,实际上这些信息对我们来说是透明的,不必关心。只须关心打开文件的操作返回一个指向FILE的指针,称为文件指针,代表创建的流。对这个流(对应的文件)的操作都是通过这个指针来进行。可以认为这种文件指针就是流的体现, 人们也把文件指针作为流的同义词。
每个c程序启动时自动创建三个流(文件指针):标准输入流(指针名stdin),标准输出流(指针名stdout)和标准错误流(指针名stderr)。
stdin 通常与操作系统的标准输入连接(如:键盘)
stdout通常与操作系统的标准输出连接(如:显示器)
stderr 通常直接连接显示器,不能重定向。
缓冲式输入输出
外存速度较慢, 一般采用成块传递方式, 一次传递一批数据。而程序里使用数据往往不一样,为了弥合程序与外存在数据操作方式和速度方面的差距,人们提出一种技术,即开辟一块存储区(称为数据缓冲区,简称缓冲区),作为文件与使用数据的程序之间的传递媒介。引入缓冲区,主要是提高程序的工作效率。
输入流操作:文件中的数据以块方式复制到缓冲区,程序需要读数据时就到缓冲区读取,不必每次访问外存,如果要读的数据已经用完,系统会自动从文件取一批数据将缓冲区填满。
输出流操作:每当缓冲区装满后自动执行对文件的成块写操作。
程序打开文件(fopen())后,系统为其分配一个缓冲区(一般为动态存储分配),数据传递都通过缓冲区进行,文件关闭时(fclose())释放缓冲区
对于文件的实操,在一下篇文章中再谈。