使用流的磁盘文件I/O—分类

一、磁盘文件I/0(作为字符串存入磁盘)

在处理磁盘文件的时候,需要另一套类:作为输入的类ifstream(派生自istream)、同时最为输入输出的类fstream(派生自iostream)、作为输出的类ofstream(派生自ostream)。

     类ifstream、fstream、ofstream在头文件fstream中进行声明。

1.首先看写入数据和读出数据

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
	char ch='x',ich;
	int j=77,ij;
	double d=32.33,id;
	string  s1="jia",is1;
	string  s2="you",is2;
	ofstream outfile("fdata.txt");
	outfile<<ch
		<<j
		<<' '
		<<d
		<<s1
		<<' '
		<<s2;
	cout<<"File written\n";
////用来读取文件fdata.txt中的数据	
//	ifstream infile("fdata.txt");
//	infile>>ich>>ij>>id>>is1>>is2;
//	cout<<ich<<endl
//		<<ij<<endl
//		<<id<<endl
//		<<is1<<endl
//		<<is2<<endl;
	return 0;
}

首先,程序中建立了类ofstream对象outfile,同时初始化文件fdata.txt。这里outfile类似于前面程序中的cout,可以使用插入运算符(<<)输出任意变量到文件中。

其次,当程序结束时,对象outfile到了作用域之外,自然会调用析构函数来关闭文件,这里就是为什么如果上面写入和读出在同一个程序中会出现乱码,因为在执行读出程序的时候,文件还没有关闭。

2.上面的例子字符串需要空格隔开,因此字符串中不能有空白符,那么含有空白符的字符串怎么处理呢?

#include <fstream>
#include <iostream>
using namespace std;
//int main()
//{
//	ofstream outfile("text.txt");
//	outfile<<"12312312312312\n"
//		<<"wqerqerqerqerqer\n"
//		<<"fjdlsgjalfjlsadfj";
//	return 0;
//}
int main()
{
	ifstream infile("text.txt");
	const int MAX=80;
	char buff[MAX];
	while(!infile.eof())
	{
		infile.getline(buff,MAX);
		cout<<buff<<endl;
	}
	return 0;
}

注释:① 每行用换行符来指定结束。注意:这是char*字符串,而不是string的对象。(?多数流运算符处理char*字符串时更为容易?)

② 读出的时候,使用istream的成员函数getline每次读入一行,包括空白符,知道遇到默认的换行符。

③ while(!infile.eof()) 可以检测EOF信号

上面只是检测eofbit没有检测failbit位和badbit为等其他错误标志,如果都检测,则 while(infile.good())

其实可以直接检测流。任何流对象(如infile)都有一个返回值用于检测错误条件,有任何错误返回0,情况正常返回非0;所以可以这么写:while(infile) 

3. 看一个字符io的例子:

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
////写入文件
//int main()
//{
//	string str="qweqweqweqweqweqwewq"
//		" lsdfaldjfaldkfja";
//	ofstream outfile("text.txt");
//	for (int j=0;j<str.size();j++)
//	{
//		outfile.put(str[j]); //首先,把字符str[j]写到流对象outfile中,直到全部写入流中
//		                     //其次,流对象把字符串写入到文件text.txt中
//	}
//	cout<<"File written\n";
//	return 0;
//}
//读出文件(用get)
int main()
{
	char ch;
	ifstream infile("text.txt");
	while(infile)
	{
		infile.get(ch); //首先,从文件text.txt中读出整个字符串到流对象infile中
		                //其次,把流对象中的字符逐个读到ch中
		cout<<ch; 
	}
	cout<<endl;
	return 0;
}
//读出文件的另一条途径:类ios的成员函数rdbuf
//功能:此函数返回与流对象相关联的streambuf(或filebuf)对象的指针,
//此相关联对象包含一个缓冲区以保存流中读出的字符
int main()
{
	ifstream infile("text.txt");
	cout<<infile.rdbuf(); //通过返回的缓冲区对象的指针来显示infile流对象中的字符串
	cout<<endl;
}

二、二进制I/O   使用格式化I/O可以写入少量数据到磁盘中,但是如果要存储大量的数据,那么使用二进制I/O最有效。格式化I/O使用字符串,而二进制I/O的数值在磁盘中的存储和计算机内存中一样。如:在二进制I/O中存储float需要四个字节;但是格式化I/O表示中,存储6.02323e13却需要十个字节。首先举个简单的例子:

#include <iostream>
#include <fstream>
using namespace std;
const int MAX=100;
int buffer[MAX];
int main()
{
	//写入文件
	for(int j=0;j<MAX;j++)
		buffer[j]=j;
	ofstream os("edata.bat",ios::binary);  //创建输出流
	os.write(reinterpret_cast<char*>(buffer),MAX*sizeof(int));
	os.close();   // 必须关闭与文件关联的流,这样第二个流才能打开文件
	//读出文件
	for(int j=0;j<MAX;j++)
		buffer[j]=0; //清除buffer
	ifstream is("edata.bat",ios::binary);
	is.read(reinterpret_cast<char*>(buffer),MAX*sizeof(int));
	for (int j=0;j<MAX;j++)
	{
		if (buffer[j]!=j)
		{
			cerr<<"data is incorrect\n"; 
			return 1;
		}
	}
	cout<<"data is correct\n";
	return 0;
}

① write和read这两个函数以字节的方式考虑数据,其参数是数据的地址及其长度。

地址必须进行强制转换,使用reinterpret_cast转化成char*类型,长度就是字节的个数。

②在使用二进制数据时,创建流对象的第二个参数的位置必须使用参数:ios::binary;

三、对象I/O

将对象写入磁盘时,一般都使用二进制模式。这样写入到磁盘中的内容其位构造和在内存中相同,并且保证了对象中所含的数值数据也可以正确的被处理。

举例:

#include<iostream>
#include <fstream>
using namespace std;
class person
{
public:
void getdata()
{
cout<<"Enter the name: "; cin>>name;
cout<<"Enter the age: "; cin>>age;
}
void showdata()
{
cout<<"Name: "<<name<<endl;
cout<<"Age: "<<age<<endl;
}
private:
char name[80];
short age;
};
//单个对象的I/O
int main()
{
//写入文件
person per;
per.getdata();
ofstream os("person.bat",ios::binary);
os.write(reinterpret_cast<char*>(&per),sizeof(per));
os.close();
//读出文件
person pers;
ifstream is("person.bat",ios::binary);
is.read(reinterpret_cast<char*>(&pers),sizeof(pers));
pers.showdata();
return 0;
}
//多个对象的I/O
int main()
{
//写入文件
char ch;
person per;
fstream file; //创建输入输出文件流对象
file.open("group.bat",ios::app|ios::out|ios::in|ios::binary);
do
{
cout<<"Enter person's data:"<<endl;
per.getdata();
file.write(reinterpret_cast<char*>(&per),sizeof(per));
cout<<"Enter another person(y/n)? ";
cin>>ch;
} while (ch=='y');
//读出文件
person pers;
file.seekg(0); //保证从文件的开头进行读
file.read(reinterpret_cast<char*>(&pers),sizeof(per));//读取第一个对象实例
while(!file.eof())
{
cout<<"person: "<<endl;
pers.showdata();
file.read(reinterpret_cast<char*>(&pers),sizeof(per));//读取下一个对象实例
}
return 0;
}

注意:①如果写入程序和读出文件为不同的函数的时候,必须要有相同的成员数据,成员函数可以不同(因为他们无需写入磁盘)。然而只有在不使用虚函数的简单类中才可以这么说。

原因:派生类的对象在内存中其数据之前包含有特殊的数值。在使用虚函数时,这些数值用来帮助确定对象的类。如果改变类的成员函数,此数值也跟着改变。

②  还不应该对带有指针数据成员的对象进行磁盘I/O,正如我们想到的,当将对象读出到其他不同内存的时候,指针值就不是正确的了。

③ 函数open

在前面的程序中都是:创建文件并且使用相同的语句初始化:如,ifstream is("person.bat",ios::binary);

但在这个多对象的I/O函数中,使用fstream file;创建文件流,另一个语句使用open把此文件打开file.open("group.bat",ios::app|ios::out|ios::in|ios::binary); ---这对于打开文件有可能出错是有用的。这样也不会存在写入后再读取的时候,必须切断写入流对象与文件的关联的麻烦。

④ 模式位

MSDN搜索:basci_fstream::open

The type is a bitmask type that describes an object that can store the opening mode for several iostreams objects. The distinct flag values (elements) are:

  • app, to seek to the end of a stream before each insertion.

  • ate, to seek to the end of a stream when its controlling object is first created.

  • binary, to read a file as a binary stream, rather than as a text stream.

  • in, to permit extraction from a stream.

  • out, to permit insertion to a stream.

  • trunc, to delete contents of an existing file when its controlling object is created.

posted @ 2011-11-09 21:44  csqlwy  阅读(815)  评论(0编辑  收藏  举报