STL学习(10):输入输出流

  • 标准输入输出流
  • 文件输入输出流
  • 字符串输入输出流

标准输入输出流

即标准输入流cin、标准输出流cout。前者指键盘,后者指显示器。

1插入符与提取符

在输入输出流类库中,重载了两种运算符以简化输入输出流的使用,运算符<<常用做输入输出流的插入符,表明“输出到”,例如cout << “Hello”是把字符串“Hello”输出到屏幕上;运算符>>常用做提取符,表明“赋值给”,例如 cin >> i 是把键盘输入的信息赋值给i。

标准输入给不同类型变量赋值的不足示例。

#include <iostream>
using namespace std;
int main() {
	int i;
	char str[20];
	cout << "请输入一个整形数及一个字符串:" ;
	cin >> i >> str;
	cout << "i=" << i << endl ;
	cout << "str=" << str << endl ;
	return 0;
}

若在命令行上输入1 how are you?,回车后执行结果为:

请输入一个整形数及一个字符串:1 how are you?
i=1
str=how

实际上在交互过程中,经常需要一次输入一行字符序列,当这些字符安全地存储到缓冲区后再进行扫描和转换工作。在该示例中,并不能把输入的串完全的放到内存中,下面讲到的get系列函数很好的解决了这个问题。

2 get系列函数  

常用的有3个函数。

(1)int get() ;    返回输入流一个字符的ASCII值。

(2)istream& get(unsigned char* pszBuf, int nBufLen, char   delim=’\n’);     

pszBuf: 指向字符缓冲区的指针,用于保存结果     nBufLen: 缓冲区长度     delim: 结束字符,根据结束字符判断何时停止读入操作

(3)istream& getline(unsigned char* pszBuf, int nBufLen, char delim=’\n’);     参数解释同get

第2个get函数及getline函数都是读一行记录,那么它们有什么区别呢?细微而重要的区别在于:当遇到输入流中的界定符(delim,即结束字符)时get()停止执行,但是并不从输入流中提取界定符,直接在字符串缓冲区尾部加结束标志’\0’;函数getline()则相反,它将从输入流中提取界定符,但仍然不会把它存储到结果缓冲区中。 

#include <iostream>
using namespace std;
int main() {
	char szBuf[60]; //定义输入字符串接收缓冲区
	cout << "请输入一个字符串:" ;
	int n = cin.get();  //先读1个字符
	cin.getline(szBuf, 60); //接着读1行字符,遇到结束符’\0’停止
    cout << n << endl;
    cout << "The received string is: " << szBuf << endl;
    return 0;
}
//输出结果
请输入一个字符串:abcde
97
The received string is: bcde

3 处理流错误  

获取状态信息的函数如下:

  • int rdstate(); 无参数,返回值既是状态信息特征值。 使用下面函数来检测相应输入/输出状态:
  • bool good();若返回值true,一切正常,没有错误发生。
  • bool eof();   若返回值true,表明已到达流的末尾。
  • bool fail();若返回值true,表明I/O操作失败,主要原因是非法数据(例如读取数字时遇到字母)。但流可以继续使用。
  • bool bad();   发生了(或许是物理上的)致命性错误,流将不能继续使用。
//判断是否输入整形数。
int main() { 
	int a;
	cout << "请输入一个数据: ";
	cin>>a;
	cout<<"状态值为: " <<cin.rdstate()<<endl;
	if(cin.good())
	{
		cout<<"输入数据的类型正确,无错误!"<<endl;
	}
	if(cin.fail())
	{
		cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl;
	}
	return 0;
}

//确保输入一个整形数给变量a。
int main() { 
	int a;
	while(1)
	{
	cin>>a;			//赋值给整形数a
	if(cin.fail())	//如果输入数据非法,则:
	{
		cout<<"输入有错!请重新输入"<<endl;
		cin.clear();//清空状态标识位
		cin.get();	//清空流缓冲区
	}
	else			//如果输入数据合法,则:
	{
		cout<<a<< endl;    //直接在屏幕上输出数据
		break;		//跳出while循环
	}
      }
return 0;
}

2 文件输入输出流

C++对文件的读写使用的是ifstream、ofstream和fstream流类,同样是依照“打开-->读写-->关闭”原语进行操作。文件读写所用到的很多常数都在基类ios中被定义出来。ifstream类只用于读取文件数据,ofstream类只用于写入数据到文件,fstream则可用于读取和写入文件的数据。

1 文件打开

ifstream/ofstream/fstream流类都使用构造函数或open函数打开文件,原型如下: 

ifstream( const char* szName, int nMode = ios::in, int nProt = filebuf::openprot )

ofstream( const char* szName, int nMode = ios::out, int nProt = filebuf::openprot )

fstream( const char* szName, int nMode, int nProt = filebuf::openprot );

void ifstream::open(const char *filename, int openmode=ios::in)

void ofstream::open(const char *filename, int openmode=ios::out|ios::trunc)

void fstream::open(const char *filename, int openmode=ios::in|ios::out)

均有两个参数,第1个是文件名,第2个是打开方式,打开方式见下述:  

  • ios::in      以读取方式打开文件
  • ios::out    以写入方式打开文件
  • ios::app   每次写入数据时,先将文件指针移到文件尾,以追加数据到尾部
  • ios::ate    仅初始时将文件指针移到文件尾,仍可在任意位置写入数据
  • ios::trunc 写入数据前,先删除文件原有内容(清空文件),当文件不存在时会创建文件
  • ios::binary以二进制方式打开文件,不作任何字符转换

2 文件关闭

ifstream/ofstream/fstream流类都通过close函数释放文件资源。 close();    无参数,一般来说打开文件与关闭文件是成对出现的。

3 文件读写

//读文本文件并显示在屏幕上
int main(){
	char szBuf[80];
	ifstream in("a.txt");//通过构造函数创建文件读入流
	if(!in) return 0;		//若文件不存在,返回
	while(in.getline(szBuf, 80)) //通过getline函数按行读取内容
	{
	cout << szBuf << endl;	//将读入缓冲区内容输出到屏幕上,判断是否正确
	}
	
	in.close();
	return 0;
}

//写文本文件:把学生成绩信息保存至文件
struct STUDENT	//学生成绩结构体
{
	char strName[20];	//姓名
	int  nGrade;		//成绩
};
int main() {
	ofstream out;
	out.open("d:\\a.txt");	//打开或创建a.txt文本文件
	STUDENT st1 ={"张三", 90};//用结构体产生两名学生的成绩信息
	STUDENT st2 ={"李四", 80};
	out << st1.strName << "\t" << st1.nGrade << endl;//把成绩信息存到文本文件
	out << st2.strName << "\t" << st2.nGrade << endl;
	out.close();
	return 0;
} 

可以看出,通过“<<”完成写文本文件是一个较好的方法,这是因为“<<”操作符可把不同类型的数据直接输出。结构体中strName是字符串,nGrade是整形数,但均可直接输出到文件上即可。此外strName与nGrade之间采用“\t”输出是为了数据对齐,而“endl”保证输出“\n”回车符,保证了文本文件的换行,是必须的。

读写二进制文件

主要是通过read()、write()函数完成读写二进制文件功能的,原型如下所示

  • ostream& write(const char *, int nSize)
  • istream& read(char *, int nSize)

第1个参数表明读写缓冲区的头指针,第二个参数表明读写缓冲区的大小

//写二进制文件:把学生成绩信息保存至文。
struct STUDENT	//学生成绩结构体
{
	char strName[20];	//姓名
	int  nGrade;		//成绩
};
int main() {
	ofstream out;
	out.open("d:\\a.txt");	//打开或创建a.txt文本文件
	STUDENT st1 ={"张三", 90};//用结构体产生两名学生的成绩信息
	STUDENT st2 ={"李四", 80};
	out.write((const char *)&st1, sizeof(STUDENT));	//把成绩信息存到二进制 文件
	out.write((const char *)&st2, sizeof(STUDENT));
	out.close();
	return 0;
}

//读二进制文件,并把结果显示在屏幕上。
int main() {
	ifstream in;
	in.open("d:\\a.txt");
	if(!in) return 0;
	STUDENT st1;
	STUDENT st2;
	in.read((char *)&st1, sizeof(STUDENT));
	in.read((char *)&st2, sizeof(STUDENT));
	cout << st1.strName << "\t" << st1.nGrade << endl;
	cout << st2.strName << "\t" << st2.nGrade << endl;
	in.close();
	return 0;
} 

输入输出流缓冲

C++标准库封装了一个缓冲区类streambuf,以供输入输出流对象使用。每个标准C++输入输出流对象都包含一个指向streambuf的指针,用户可以通过调用rdbuf()成员函数获得该指针,从而直接访问底层streambuf对象,可以直接对底层缓冲区进行数据读写,从而跳过上层的格式化输入输出操作。但由于类似的功能均可由上层缓冲区类实现,所以就不再加以论述了。streambuf最精彩的部分在于它重载了operator <<及operator >>。对operator <<来说,它以streambuf指针为参数,实现把streambuf对象中的所有字符输出到输出流出中;对operator >>来说,可把输入流对象中的所有字符输入到streambuf对象中。

//打开一个文件并把文件中的内容送到标准输出中。
int main() {
    ifstream fin("d:\\my.txt");
	if(fin !=NULL)			//判断文件是否存在
	{
		cout<<fin.rdbuf();  //把文件所有内容输出到屏幕上
	}
	fin.close();	
}

定位输入输出流

流的位置标识有3个:

  • ios::beg        流的开始位置
  • ios::cur        流的当前位置
  • ios::end        流的结束位置 定位函数主要有两个:

istream& seekg(long relativepos, ios::seek_dir dir)

针对输入流。第1个参数是要移动的字符数目,可正可负;第2个参数是移动方向,是ios::begin, ios::cur, ios::end中的一个值。含义是:字符指针相对于移动方向移动了向前或向后移动了多少个字符。

ostream& seekp(long relativepos, ios::seek_dir dir) 针对输出流。参数含义同seekg。

//下面示例先写文件,然后再把文件内容读出显示在屏幕上。
int main() {
	fstream	in_out;
	in_out.open("d:\\my.txt",ios::in|ios::out|ios::trunc);
	in_out.write("Hello", 5);
	in_out.seekg(0, ios::beg);  //读指针移到文件头
	cout << in_out.rdbuf();
	in_out.close();
	return 0;
}

3 字符串输入输出流

标准库定义了三种类型的字符串流。

  • stringstream    输入流,提供读string功能
  • ostringstream  输出流,提供写string功能
  • stringstream    输入输出流,读写string功能
//反解字符串给各变量赋值
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
	int n;
	float f;
	string strHello;
	string strText = "1 3.14 hello";
	istringstream s(strText);
	s >> n;
	s >> f;
	s >> strHello;
	cout << "n= " << n << endl;
	cout << "f= " << f << endl;
	cout << "strHello= " << strHello << endl;
	return 0;
}
//合并不同类型的数据到字符串
int main()
{
	cout << "type an int,a float and a string: ";
	int i;
	float f;
	string stuff;
	cin >> i >> f;
	getline(cin, stuff);
	ostringstream	os;
	os << "integer= " << i << endl;
	os << "float= " << f << endl;
	os << "string= " << stuff << endl;
	string result = os.str();
	cout << result << endl;
	return 0;
}

综合示例

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
	string strName;		//姓名
	string strSex;		//性别
	int	   nAge;		//年龄
};
istream& operator>> (istream& is, Student& s)
{
	is >> s.strName >> s.strSex >> s.nAge;
	return is;
}
ostream& operator << (ostream& os, Student& s)		//Student是普通对象
{
	os << s.strName << "\t" << s.strSex << "\t" << s.nAge << "\n";
	return os;
}
ostream& operator << (ostream& os, const Student& s)//Student是常对象
{
	cout << "const Student输出是:" << endl;
	os << s.strName << "\t" << s.strSex << "\t" << s.nAge << "\n";
	return os;
}
void f(const Student& s)
{
	cout << s;
}
int main(int argc, char *argv[])
{
	Student s;
	cout << "请输入数据(姓名 性别 年龄):";
	cin >> s;
	cout << "输入的数据是: ";
	cout << s;
	f(s);    
	return 0;
}	

对该程序着重理解主程序中cin>>s, cout << s的用法,它们均是对对象的操作。当cin >> s时,其实真正调用的是重载的istream& operator>> (istream& is, Student& s),如何输入是由该函数体来完成的;同理当cout << s时,真正调用的是重载的ostream& operator << (ostream& os, Student& s),如何输出由该函数体来完成;当Student是常对象时,例如函数void f(const Student& s)中cout<<s调用的是ostream& operator << (ostream& os, const Student& s)。那么为什么对基本数据类型就不必重载相应函数呢?那是因为STL中缺省实现了基本数据类型的输入输出函数,我们就不必要再重载了。

猜你喜欢

转载自blog.csdn.net/QQ2558030393/article/details/93396285