C++文件操作

 

文件的概念

对于用户来说,常用到的文件有两大类:程序文件和数据文件。而根据文件中数据的组织方式,则可以将文件分为ASCII 文件和二进制文件。

ASCII 文件,又称字符(character)文件或者文本文件,它的每一个字节放一个 ASCII 代码,代表一个字符。ASCII 文件中数据与字符一一对应,一个字节代表一个字符,可以直接在屏幕上显示或打印出来,这种方式使用方便,比较直观,便于阅读,但一般占用存储空间较大,而且输出时要将二进制转化为 ASCII 码比较花费时间。

二进制文件,由二进制数组成。又称内部格式文件或字节文件,是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。二进制文件,输出时不需要进行转化,直接将内存中的形式输出到文件中,占用存储空间较小,但一个字节并不对应一个文件,不能直观显示文件中的内容。

 

文件流和文件流对象

文件流是以外存文件未输入输出对象的数据流。输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据。每一个文件流都有一个内存缓冲区与之对应。

 

C++中的IO(input-output、输入与输出)流

C++流:信息从外部输入设备(如键盘)向计算机内部(如内存) 输入 和 从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。

流的特性是:有序连续、具有方向性。

流可以分为两种:

1)文本流:在缓冲区中读出和写入时是基于ASCII或者Unicode字符编码的。写入缓冲区的最大长度规定为254个字符。在文本文件结束时规定以一个回车符和换行符结尾,在写入缓冲区时,会将\n转换为回车换行符,在从缓冲区中读取出数据时,就会将回车换行符转换为换行符。

2)二进制流:二进制的流在向缓冲区中写入或读出文件时,不进行字符的转换,直接从设备或文件中读取,在这个过程中不发生任何改变,原先是什么样子,读取之后还是什么样子,它是基于二进制数字的。

C++中的三种操作:标准IO、文件IO、串IO。

标准IO:兼容C中的输入输出,以键盘和屏幕为操作对象,从键盘输入或从屏幕输出 。

文件IO:以磁盘文件为输入输出的对象,数据在磁盘中写入和读出 。

串IO:对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(该空间可以存储任何信息)。这种输入和输出称为字符串输入输出,简称串I/O。

C++ 标准库中还专门提供了 3 个类用于实现文件操作,它们统称为文件流类,这 3 个类分别为:

ifstream:专用于从文件中读取数据;

ofstream:专用于向文件中写入数据;

fstream:既可用于从文件中读取数据,又可用于向文件中写入数据。

值得一提的是,这 3 个文件流类都位于 <fstream> 头文件中,因此在使用它们之前,程序中应先引入此头文件。

 

C++在类库中定义了四个全局流的对象:

cin : 标准输入流对象,对应键盘;

cout:标准输出流对象,对应显示器;

cerr:标准输出错误流对象(无缓冲),对应显示器;

clog:标准输出错误流对象(缓冲),对应显示器;

在新库中 要使用这四个功能,必须包含文件并引入std标准命名空间。

注意:

1.使用cin输入时,从键盘输入的数据会存放在键盘的缓冲区。当我们按下回车时,就会将其送入输入缓冲区。所以对输入的修改只能在按下回车之前。一旦按下回车就无法修改,因为在输入缓冲区中,只有当数据被全部取完时,需要重新输入,写入缓冲区的数据,会顺序依次被取出,无法删除。

2.输入与输出的数据的类型必须一致,否则会报错。出错时会在status状态字的对应位置置1,程序继续运行。

3.在对于字符型或字符串进行cin时,字符串中不能有空格,而且回车符也无法起作用。当遇到回车时就直接结束,遇到空格,空格后输入的字符无效。

 

要以磁盘文件为对象进行输入输出,必须定义一个文件流类的对象,通过文件流对象将数据从内存输出到磁盘文件,或者将磁盘文件输入到内存。

 

定义文件流对象后,我们还需要将文件流对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件,并确定文件的工作方式(输入还是输出,二进制还是 ASCII)。我们可以在定义流对象的时候指定参数来调用构造函数,或者通过成员函数 open 来进行文件流对象和指定文件的关联。

 

使用文件的过程是固定的,一般步骤如下:

(1) 打开一个文件,使磁盘文件和文件流对象建立联系;

(2) 将数据写入一个文件,就如同cout用于向显示器送数据。以后可从这个文件读取数据,就如同cin用于键盘输入。

(3) 当不再使用文件时,要关闭文件,此时文件将从缓冲区中完全写回磁盘。这样,可以永久保存数据。

相关具体细节说明:

☆对文件进行操作,必须在程序前增加一句: #include<fstream>

☆打开文件:使得文件流对象和磁盘文件之间建立联系

a、通过构造函数     

//参数:文件名,请求标志位

std::ofstream ofile("test.txt",std::ofstream::out);

b、使用成员函数open。如果该文件已经打开,open会调用失败。

std::ofstream ofile;

ofile.open("test.txt",std::ofstream::out)

每个类都有对于文件的访问权限操方式的限制,输入输出的请求标志:

      in、input:打开文件读,内部流缓冲区支持输入操作

      out、output:文件打开供写入,内部流缓冲区支持输出操作

      binary:执行二进制操作

      app、append:追加,所有的操作都发生在文件的末尾

      ate、atend:输出位置在文件的末尾

      trunc、trucate:打开文件之前存于文件之中的内容都会被丢弃

☆关闭文件:断开当前文件与流对象之间的关联

ofile.close();

☆进行操作时 可以用插入 >> 运算符 和 提取 << 运算符进行         

ofile << "sdasdas";     //表示向文件中输入一个数

char a[1024];

istream ifile("test1.txt",std::ifstream::in);

ifile >> a;  //将文件中的数据写入a中

☆写入

ofstream 类中提供了write成员函数,用于向文件中写入数据

ostream& write (const char* s, streamsize n);

☆读取

 ifstream 类中提供了read成员,用于从文件中读取数据

istream& read (char* s, streamsize n);

在对二进制读写时,我们采用write 和 read 成员函数进行操作。

在类中还提供了一些其他的成员函数入 put()(单个字符插入到流中) get() (从流中读取单个字符)等。

先介绍文本文件的操作。

例1、将百鸡问题计算结果存入d盘myfile.txt文件
#include<fstream>
#include<iomanip>
using namespace std;
int main(){
    int i,j,k;
    ofstream ofile;       //定义输出文件
    ofile.open("d:\\myfile.txt");     //作为输出文件打开
    ofile<<"   公鸡      母鸡      小鸡"<<endl;   //标题写入文件
    for(i=0;i<=20;i++)
        for(j=0;j<=33;j++){
            k=100-i-j;
            if((5*i+3*j+k/3==100)&&(k%3==0))       //注意有(k%3==0)
            ofile<<setw(6)<<i<<setw(10)<<j<<setw(10)<<k<<endl;   //数据写入文件
        }
    ofile.close();      //关闭文件
           
    return 0;
}
注意:在c++中字符\要用转义字符\\表示。

运行之,在d盘中生成一个文件myfile.txt,参见下图:

C++文件操作

 

例2、读出上例存放百鸡问题计算结果的文件myfile.txt的内容
#include<fstream>
#include<iostream>
#include<iomanip>
using namespace std;
int main(){
    char a[28];
    ifstream ifile;               //定义输入文件
    ifile.open("d:\\myfile.txt");     //作为输入文件打开
    int i=0,j,k;
    while(ifile.get(a[i])){      //读标题,请对比cin.get(),不可用>>,它不能读白字符
        if(a[i]=='\n') break;
        i++;
    }
    a[i]='\0';
    cout<<a<<endl;
    while(1){
        ifile>>i>>j>>k;                //由文件读入数据
        if(ifile.eof()!=0) break;            //当读到文件结束时,ifile.eof()为真
        cout<<setw(6)<<i<<setw(10)<<j<<setw(10)<<k<<endl;     //屏幕显示       
    }
    ifile.close();                 //关闭文件
    return 0;
}

运行之,参见下图:

C++文件操作

提示
☆打开文件时,如磁盘文件不存在,会自动建立文件,但指定目录必须存在,否则建立文件失败。
☆磁盘文件操作与键盘、显示器操作非常相似。例题中用输出文件流对象(如ofile)代替cout,输入文件流对象(如ifile)代替cin,数据的去向和来源则由显示器和键盘变为磁盘文件。
☆对文件进行操作,必须在程序前增加一句: #include<fstream>

上面主要介绍讨论文本文件的使用方法和操作过程。下面介绍二进制文件使用方法和操作过程。要实现以二进制形式读写文件,<< 和 >> 将不再适用。

1)get()函数 :get函数有三种操作形式

file2.get(x)

x=file2.get()

file2.get(str1,127,‘A’)

2)put()函数

3)为顺序读写数据特殊设计的成员函数:write 和 read

write ( char * buffer, streamsize size );

read ( char * buffer, streamsize size );

这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数(size值很重要,因为二进制文件内容没有行的概念(’\n’),字节之间是紧挨着的)。

4)随机读写文件

  通过移动文件读写指针,可在文件指定位置进行读写。

  seekg(绝对位置);      //绝对移动,    //输入流操作

  seekg(相对位置,参照位置);  //相对操作

  tellg();          //返回当前指针位置

  seekp(绝对位置);      //绝对移动,    //输出流操作

  seekp(相对位置,参照位置);  //相对操作   

  tellp();          //返回当前指针位置

  参照位置:

  ios::beg  = 0       //相对于文件头

  ios::cur  = 1       //相对于当前位置

  ios::end  = 2       //相对于文件尾

总结:打开二进制文件时,如可直接定义输入输出而不是分开进行定义,如:

fstream iofile(“stud.dat”,ios::in|ios::out|ios::binary|ios::trunc);

其中若最初无stud.dat文件,则需添加ios::trunc来创建一个新文件;

打开文件后,用if(!iofile){cerr<<“error!”<<endl; exit(1);}来判断文件是否打开成功;

对二进制文件,普通字符用get()和Put()函数进行操作,对数据块,可以用write()和read()函数操作,使用<<,>>运算符只能进行文本文件的读写操作,用于二进制文件可能会产生错误。经常和read配合使用的函数是gcount(),用来获得实际读取的字节数。使用eof()函数检测文件是否读结束,使用gcount()获得实际读取的字节数。

二进制文件操作示例,代码如下:
/*
 有几个学生的数据,要求:
把它们存到磁盘文件中;
将磁盘文件中的第1,3,5个学生数据读入程序,并显示出来;
将2第个学生的数据修改后存回磁盘文件中的原有位置。
从磁盘文件读入修改后的个学生的数据并显示出来。*/
#include<iostream>
#include<fstream>
#include <cstring>
using namespace std;

struct student
{
    char name[20];
    int age;
    char id[20];
    char sex[10];
};
int main()
{
    student stu1[5] = { {"张三",17,"2018212114","男"},
                    {"李四",19,"2018212115","男"},
                    {"王五",20,"2018212113","男"},
                    {"aaa",19,"2018212116","男"},
                     {"nvzi",18,"2018212110","女"} };
    fstream iofile("stud.dat", ios::in | ios::out | ios::binary | ios::trunc);
    if (!iofile)
    {
        cerr << "error!" << endl;
        exit(1);
    }
    int i;
    for (i = 0; i < 5; i++)
        iofile.write((char*)&stu1[i], sizeof(stu1[i]));
    
    student stu2[3];
    for (i = 0; i < 5; i=i+2)
    {
        iofile.seekg(i * sizeof(stu2[i/2]), ios::beg);
        iofile.read((char*)&stu2[i/2], sizeof(stu2[i/2]));
    }
    
    for (i = 0; i < 5; i=i+2)
    {
        cout << "第" <<i+1<< "位同学:" << endl;
        cout << stu2[i/2].name << "  "<< stu2[i/2].age << "  " <<stu2[i/2].id << "  "<<stu2[i/2].sex << endl;
    }
    stu1[1].age = 18;
    strcpy(stu1[1].name, "goto");
    strcpy(stu1[1].sex, "女");
    iofile.seekp(sizeof(stu1[1]), ios::beg);
    iofile.write((char*)&stu1[1], sizeof(stu1[1]));
    iofile.seekg(0, ios::beg);
    student stu3[5];
    for (i = 0; i < 5; i++)
        iofile.read((char*)&stu3[i], sizeof(stu3[i]));
    for (i = 0; i < 5; i++)
    {
        cout << "第" << i + 1 << "位同学:" << endl;
        cout << stu3[i].name << "  " << stu3[i].age << "  " << stu3[i].id << "  " << stu3[i].sex << endl;
    }
    iofile.close();
    return 0;
}

运行之,参见下图:

C++文件操作

在当前目录下建立文件stud.dat,请注意例子中的这句fstream iofile("stud.dat", ios::in | ios::out | ios::binary | ios::trunc);

 

 


 

 

相关文章: