【转载】C++文件操作(一)

来源:互联网 发布:58同城淘宝客服 编辑:程序博客网 时间:2024/05/17 08:28

1. 文件的概念

    以前进行的输入输出操作都是在键盘和显示器上进行的,通过键盘向程序输入待处理的数据,通过显示器输出程序运行过程中需要告诉用户的信息。键盘是C++系统中的标准输入设备,用cin流表示,显示器是C++系统中的标准输出设备,用cout流表示。

    数据的输入和输出除了可以在键盘和显示器上进行之外,还可以在磁盘上进行。磁盘是外部存储器,它能够永久保存信息,并能够被重新读写和携带使用。所以若用户需要把信息保存起来,以便下次使用,则必须把它存储到外存磁盘上。

    在 磁盘上保存的信息是按文件的形式组织的,每个文件都对应一个文件名,并且属于某个物理盘或逻辑盘的目录层次结构中一个确定的目录之下。一个文件名由文件主 名和扩展名两部分组成,它们之间用圆点(即小数点)分开,扩展名可以省略,当省略时也要省略掉前面的圆点。文件主名是由用户命名的一个有效的C++标识 符,为了同其他软件系统兼容,一般让文件主名为不超过8个有效字符的标识符,同时为了便于记忆和使用,最好使文件主名的含义与所存的文件内容相一致。文件 扩展名也是由用户命名的、1至3个字符组成的、有效的C++标识符,通常用它来区分文件的类型。如在C++系统中,用扩展名h表示头文件,用扩展名cpp 表示程序文件,用obj表示程序文件被编译后生成的目标文件,用exe表示连接整个程序中所有目标文件后生成的可执行文件。对于用户建立的用于保存数据的 文件,通常用dat表示扩展名,若它是由字符构成的文本文件则也用txt作为扩展名,若它是由字节构成的、能够进行随机存取的内部格式文件则可用ran表 示扩展名。

    在 C++程序中使用的保存数据的文件按存储格式分为两种类型,一种为字符格式文件,简称字符文件,另一种为内部格式文件,简称字节文件。字符文件又称 ASCII码文件或文本文件,字节文件又称二进制文件。在字符文件中,每个字节单元的内容为字符的ASCII码,被读出后能够直接送到显示器或打印机上显 示或打印出对应的字符,供人们直接阅读。在字节文件中,文件内容是数据的内部表示,是从内存中直接复制过来的。当然对于字符信息,数据的内部表示就是 ASCII码表示,所以在字符文件和在字节文件中保存的字符信息没有差别,但对于数值信息,数据的内部表示和ASCII码表示截然不同,所以在字符文件和 在字节文件中保存的数值信息也截然不同。如对于一个短整型数1069,它的内部表示占有两个字节,对应的十六进制编码为04 2D,其中04为高字节值,2D为低字节值;若用ASCII码表示则为四个字节,每个字节依次为1069中每个字符的ASCII码,对应的十六进制编码为 31 30 36 39。当从内存向字符文件输出数值数据时需要自动转换成它的ASCII码表示,相反,当从字符文件向内存输入数值数据时也需要自动将它转换为内部表示,而 对于字节文件的输入输出则不需要转换,仅是内外存信息的直接拷贝,显然比字符文件的输入输出要快得多。所以当建立的文件主要是为了进行数据处理时,则适宜 建立成字节文件,若主要是为了输出到显示器或打印机供人们阅读,或者是为了供其他软件使用时,则适宜建立成字符文件。另外,当向字符文件输出一个换行符/n时,则将被看作为输出了回车/r和换行/n两个字符;相反,当从字符文件中读取回车和换行两个连续字符时,也被看作为一个换行符读取。

    C++程序文件,利用其他各种语言编写的程序文件,用户建立的各种文本文件,各种软件系统中的帮助文件等,因都是ASCII码文件,所以都可以在C++中作为字符文件使用。

    C++ 系统把各种外部设备也看作为相应的文件。如把标准输入设备键盘和标准输出设备显示器看作为标准输入输出文件,其文件名(又称设备名)为con,当向它输出 信息时就是输出到显示器,当从它输入信息时就是从键盘输入。标准输入输出文件con对应两个系统预定义的流,即标准输入流cin和标准输出流cout,分 别用于键盘输入和显示器输出。由于键盘和显示器都属于字符设备,所以它们都是字符格式文件。以后对字符文件所介绍的访问操作也同样适应于键盘和显示器,而 以前介绍的对键盘(cin)和显示器(cout)的访问操作也同样适应于所有字符文件。

    无 论是字符文件还是字节文件,在访问它之前都要定义一个文件流类的对象,并用该对象打开它,以后对该对象的访问操作就是对被它打开文件的访问操作。对文件操 作结束后,再用该对象关闭它。对文件的访问操作包括输入和输出两种操作,输入操作是指从外部文件向内存变量输入数据,实际上是系统先把文件内容读入到该文 件的内存缓冲区中,然后再从内存缓冲区中取出数据并赋给相应的内存变量,用于输入操作的文件称为输入文件。对文件的输出操作是指把内存变量或表达式的值写 入到外部文件中,实际上是先写入到该文件的内存缓冲区中,待缓冲区被写满后,再由系统一次写入到外部文件中,用于输出操作的文件称为输出文件。

    一 个文件中保存的内容是按字节从数值0开始顺序编址的,文件开始位置的字节地址为0,文件内容的最后一个字节的地址为n-1(假定文件长度为n,即文件中所 包含的字节数),文件最后存放的文件结束符的地址为n,它也是该文件的长度值。当一个文件为空时,其开始位置和最后位置(即文件结束符位置)同为0地址位 置。

    对 于每个打开的文件,都存在着一个文件指针,初始指向一个隐含的位置,该位置由具体打开方式决定。每次对文件写入或读出信息都是从当前文件指针所指的位置开 始的,当写入或读出若干个字节后,文件指针就后移相应多个字节。当文件指针移动到最后,读出的是文件结束符时,则将使流对象调用eof()成员函数返回非 0值(通常为1),当然读出的是文件内容时将返回0。文件结束符占有一个字节,其值为-1,在ios类中把EOF常量定义为-1。若利用字符变量依次读取 字符文件中的每个字符,当读取到的字符等于文件结束符EOF时则表示文件访问结束。

    要 在程序中使用文件时,首先要在开始包含#include<fstream.h>命令。由它提供的输入文件流类ifstream、输出文件流类 ofstream和输入输出文件流类fstream定义用户所需要的文件流对象,然后利用该对象调用相应类中的open成员函数,按照一定的打开方式打开 一个文件。文件被打开后,就可以通过流对象访问它了,访问结束后再通过流对象关闭它。

    每个文件流类都有一个open成员函数,并且具有完全相同的声明格式,具体声明格式为:

        void open(const char* fname, int mode);

    其 中fname参数用于指向要打开文件的文件名字符串,该字符串内可以带有盘符和路径名,若省略盘符和路径名则隐含为当前盘和当前路径,mode参数用于指 定打开文件的方式,对应的实参是ios类中定义的open_mode枚举类型中的枚举常量,或由这些枚举常量构成的按位或表达式。

    open_mode枚举类型中的每个枚举常量的含义如下:

    ios::in          //使文件只用于数据输入,即从中读取数据。

    ios::out         //使文件只用于数据输出,即向它写入数据。

ios::ate         //使文件指针移至文件尾,即最后位置。

    ios::app         //使文件指针移至文件尾,并只允许向文件尾输出(即追加)数据。

    ios::trunc       //若打开的文件存在,则清除其全部内容,使之变为空文件。

    ios::nocreate    //若打开的文件不存在则不建立它,返回打开失败信息。

    ios::noreplace   //若打开的文件存在则返回打开失败信息。

    ios::binary      //规定打开的为二进制文件,否则打开的为字符文件。

下面对文件的打开方式作几点说明:

    (1) 文件的打开方式可以为上述的一个枚举常量,也可以为多个枚举常量构成的按位或表达式。如:

    ios::in | ios::nocreate   //规定打开的文件是输入文件,若文件不存在则返回

                              //打开失败信息。

    ios::in | ios::out        //规定打开的文件同时用于输入和输出。

ios::app | ios::nocreate //规定只向打开的文件尾追加数据,若文件不存在

                          //则返回打开失败信息。

    ios::out | ios::noreplace //规定打开的文件是输出文件,若文件存在则返回

                               //打开失败信息。

ios::in | ios::out | ios::binary //规定打开的文件是二进制文件,并可同时

                                      //用于输入和输出。

    (2) 使用open成员函数打开一个文件时,若由字符指针参数所指定的文件不存在,则就建立该文件,当然建立的新文件是一个长度为0的空文件,但若打开方式参数中含有ios::nocreate选项,则不建立新文件,并且返回打开失败信息。

    (3) 当打开方式中不含有ios::ate或ios::app选项时,则文件指针被自动移到文件的开始位置,即字节地址为0的位置。当打开方式中含有 ios::out选项,但不含有ios::in,ios::ate或ios::app选项时,若打开的文件存在,则原有内容被清除,使之变为一个空文件。

    (4) 当用输入文件流对象调用open成员函数打开一个文件时,打开方式参数可以省略,缺省按ios::in方式打开,若打开方式参数中不含有ios::in选 项时,则会自动被加上。当用输出文件流对象调用open成员函数打开一个文件时,打开方式参数也可以省略,缺省按ios::out方式打开,若打开方式参 数中不含有ios::out选项时,则也会自动被加上。

    下面给出定义文件流对象和打开文件的一些例子:

    (1) ofstream fout;

        fout.open(a://xxk.dat); //字符串中的双反斜线表示一个反斜线

(2) ifstream fin;

    fin.open(a://wr.dat, ios::in | ios::nocreate);

    (3) ofstream ofs;

        ofs.open(a://xxk.dat, ios::app);

(4) fstream fio;

    fio.open(a://abc.ran, ios::in | ios::out | ios::binary);

    例 子(1)首先定义了一个输出文件流对象fout,使系统为其分配一个文件缓冲区,然后调用open成员函数打开a盘上的xxk.dat文件,由于调用的成 员函数省略了打开方式参数,所以采用缺省的ios::out方式。执行这个调用时,若a:xxk.dat文件存在,则清除该文件内容,使之成为一个空文 件,若该文件不存在,则就在a盘上建立名为xxk.dat的空文件。通过fout流打开a:xxk.dat文件后,以后对fout流的输出操作就是对 a:xxk.dat文件的输出操作。

    例子(2)首先定义了一个输入文件流对象fin,并使其在内存中得到一个文件缓冲区,然后打开a盘上的wr.dat文件,并规定以输入方式进行访问,若该文件不存在则不建立新文件,使打开该文件的操作失败,此时由fin带回0值,由(!fin)是否为真判断打开是否失败。

    例子(3)首先定义了一个输出文件流对象ofs,同样在内存中得到一个文件缓冲区,然后打开a盘上已存在的xxk.dat文件,并规定以追加数据的方式访问,即不破坏原有文件中的内容,只允许向尾部写入新的数据。

    例子(4)首先定义了一个输入输出文件流对象fio,同样在内存中得到一个文件缓冲区,然后按输入和输出方式打开a盘上的abc.ran二进制文件。此后既可以按字节向该文件写入信息,又可以从该文件读出信息。

    在 每一种文件流类中,既定义有无参构造函数,又定义有带参构造函数,并且所带参数与open成员函数所带参数完全相同。当定义一个带有实参表的文件流对象 时,将自动调用相应的带参构造函数,打开第一个实参所指向的文件,并规定按第二个实参所给的打开方式进行操作。所以它同先定义不带参数的文件流对象,后通 过流对象调用open成员函数打开文件的功能完全相同。对于上述给出的四个例子,依次与下面的文件流定义语句功能相同。

    (1) ofstream fout(a://xxk.dat);

    (2) ifstream fin(a://wr.dat, ios::in | ios::nocreate);

    (3) ofstream ofs(a://xxk.dat, ios::app);

    (4) fstream ofs(a:/abc.ran, ios::in | ios::out | ios::binary);

    每 个文件流类中都提供有一个关闭文件的成员函数close(),当打开的文件操作结束后,就需要关闭它,使文件流与对应的物理文件断开联系,并能够保证最后 输出到文件缓冲区中的内容,无论是否已满,都将立即写入到对应的物理文件中。文件流对应的文件被关闭后,还可以利用该文件流调用open成员函数打开其他 的文件。

   关闭任何一个流对象所对应的文件,就是用这个流对象调用close()成员函数即可。如要关闭fout流所对应的a:/xxk.dat文件,则关闭语句为:

        fout.close();