在 C++ 的标准模板库中提供了一组模板类来支持面向对象的数据的输入输出功能,如基本的输入输出流 istream类/ostream类,文件输入输出流 ifstream类/ofstream类/fstream类,字符串输入输出流 stringstream类/istringstream类/ostringstream类等。C++ I/O 还可以对对象进行输入输出操作,这些都是 C 所不具备的。这些流都位于名词空间 std 内,一般都是以 stream为后缀。它们在数据流的处理上具有灵活、完善和强大的功能。
无需多说,上图就什么都清楚了。
图一:各流类之间的继承关系
以及给出必要的定义:
typedef basic_ios<char, char_traits<char> > ios;
typedef basic_streambuf<char, char_traits<char> > streambuf;
typedef basic_istream<char, char_traits<char> > istream;
typedef basic_ostream<char, char_traits<char> > ostream;
typedef basic_iostream<char, char_traits<char> > iostream;
typedef basic_stringbuf<char, char_traits<char>,allocator<char> > stringbuf;
typedef basic_istringstream<char, char_traits<char>,allocator<char> > istringstream;
typedef basic_ostringstream<char, char_traits<char>,allocator<char> > ostringstream;
typedef basic_stringstream<char, char_traits<char>,allocator<char> > stringstream;
typedef basic_filebuf<char, char_traits<char> > filebuf;
typedef basic_ifstream<char, char_traits<char> > ifstream;
typedef basic_ofstream<char, char_traits<char> > ofstream;
typedef basic_fstream<char, char_traits<char> > fstream;
对于流类间的关系有了个概况后,接下来进入正文。
#include <iostream> using namespace std; int main() { int i; float f; char s[20]; cin>>i; cin>>f; cin>>s; cout<<"i="<<i<<endl; cout<<"f="<<f<<endl; cout<<"s="<<s<<endl; return 0; }其中,插入符"<<"和提取符">>"可连续使用。对于 cin 而言,数据默认以空格分隔,上面程序中就可以使用 cin>>i>>f>>s 来赋值,可以使用 cout<<"i="<<i<<endl<<"f="<<f<<endl<<"s="<<s<<endl 来输出。
(1)get();
读取输入流第一个字符的ASCII值。
(2)istream& get(unsigned char * pszBuf,int nBufLen,char delim='\n');
pszBuf:指向字符串缓冲区的指针,用于保存读取的结果。
nBufLen:指定缓冲区大小。
delim:指定分隔符,可不填,此时默认会'\n'。
(3)istream& getline(unsigned char * pszBuf,int nBufLen,char delim='\n');
参数解释同(2),区别在于(2)遇到分隔符即停止执行,不读取分隔符,而(3)将会读取该分隔符,但是它不会将其存入结果缓存中。
仔细观察下面两段代码的区别,比较结果,体会(2)、(3)的区别。
#include <iostream> using namespace std; int main () { char s1[100],s2[100]; cout<<"输入:?How are you? (Tab键) Fine,thank you!"<<endl; int a=cin.get(); cin.get(s1,sizeof(s1),'\t'); cin.getline(s2,sizeof(s2),'\n'); cout<<"a:"<<a<<endl<<"s1:"<<s1<<endl<<"s2:"<<s2<<endl; return 0; }
//注意!只有 get() 和 getline()位置发生变化。 #include <iostream> using namespace std; int main () { char s1[100],s2[100]; cout<<"输入:?How are you? (Tab键) Fine,thank you!"<<endl; int a=cin.get(); cin.getline(s1,sizeof(s1),'\t'); cin.get(s2,sizeof(s2),'\n'); cout<<"a:"<<a<<endl<<"s1:"<<s1<<endl<<"s2:"<<s2<<endl; return 0; }第一段代码执行结果为:
#include <iostream> using namespace std; int main () { int a; cin>>a; cout<<"输入状态信息码:"<<cin.rdstate()<<endl; if(cin.eof()) { cout<<"已到达流尾!"<<endl; } else { if(cin.good()) { cout<<"输入正确!"<<endl; } if(cin.fail()) { cout<<"输入数据类型错误!"<<endl; } if(cin.bad()) { cout<<"致命错误!"<<endl; } } return 0; }输入:1
#include <iostream> using namespace std; int main () { int d[5]; int n=0; while(n<5) { cin>>d[n]; if(cin.fail()) { cout<<"数据类型出错!将该数据抛弃并继续读取!"<<endl; cin.clear(); //清空状态标志位 cin.get(); //将这个错误数据从流中读出以抛弃 } else { cout<<"已正确读取前"<<n+1<<"个数字: "; for(int i=0;i<=n;i++) cout<<d[i]; cout<<endl; n++; } } return 0; }输入:
1.读写文本文件
文本文件是最常见的操作对象,关键是要解决如何按行读、如何按行写的问题。#include <iostream> #include <fstream> using namespace std; int main () { char s[100]; ofstream fout("test.txt"); fout<<"Hello World!"<<endl<<"How are you?"<<endl<<"Fine,thank you!"; fout.close(); ifstream fin("test.txt"); if(!fin) { cout<<"文件不存在!"; return 0; } else { while(!fin.eof()) { fin.getline(s,sizeof(s)); cout<<s<<endl; } } return 0; }
2.读写二进制文件
二进制文件读写也经常会用到,使用下面的函数来进行二进制文件读写。#include <iostream> #include <fstream> using namespace std; struct STUDENT { char name[20]; int ID; }; int main () { STUDENT t1={"张三",15}; STUDENT t2; ofstream fout("test.txt"); fout.write((const char*)&t1,sizeof(t1)); fout.close(); ifstream fin("test.txt"); if(!fin) { cout<<"文件打开失败!"; return 0; } else { fin.read((char*)&t2,sizeof(t2)); cout<<t2.name<<endl<<t2.ID; fin.close(); } return 0; }
3.输入输出流缓冲
C++ 标准库封装了一个缓冲区类 steambuf,以供输入输出流对象使用。每个标准 C++ 输入输出流对象都包含一个纸箱 streambuf 的指针,用户可以通过调用 rdbuf() 成员函数来获得该指针,从而直接访问底层 streambuf 对象;可以直接对底层缓冲进行数据读写,从而跳过上层的格式化输入输出操作。但由于类似的功能均可由上层缓冲区实现,因此就不加以论述了。streambuf 最精彩的部分在于它重载了 operator<< 和 operator>>。对于 operator<< 来说,它以 streambuf 指针为参数,实现把 streambuf 对象中的所有字符输出到输出流中;对 operator>> 来说,可把输入流对象中的所有字符输出到 streambuf 对象中。#include <iostream> #include <fstream> using namespace std; int main () { ifstream fin("test.txt"); if(fin) { cout<<fin.rdbuf(); } fin.close(); return 0; }同样是将文件内容输出,这个方法是很简洁的。当然,如果把 cout<<fin.rdbuf() 左侧的标准输出流改成文件输出流,则可实现文件的赋值功能等。
4.定位输入输出流
流的位置标识有三个:#include <iostream> #include <fstream> using namespace std; int main () { fstream fout("test.txt",ios::in|ios::out|ios::trunc); fout.write("How are you?",12); fout.seekp(0,ios::beg); //将指针移到文件头 cout<<fout.rdbuf()<<endl; fout.seekp(4); //将指针移到流的第四个位置 cout<<fout.rdbuf()<<endl; fout.seekp(0,ios::end); //将指针移到文件尾 cout<<fout.rdbuf(); fout.close(); return 0; }输出:
#include <iostream> #include <sstream> using namespace std; int main () { int a; float b; string c; char d[20]; string text="1 3.14 hello 你好"; string temp; istringstream sin(text); //反解字符串 sin>>a; sin>>b; sin>>c; sin>>d; cout<<a<<endl<<b<<endl<<c<<endl<<d; //合并基本类型 ostringstream sout; sout<<a<<" "<<b<<" "<<c<<" "<<d<<endl; cout<<sout.str(); return 0; }