把接收输出数据的地方叫做目标,把输入数据来自的地方叫做源。
C++的流类库由几个I/O操作的基础类和几个支持特定种类源和目标的I/O操作的类组成。
这些基础类在头文件 <iostream>
中说明。ios类中的一个指针成员指向streambuf类的对象,streambuf类管理一个流的缓冲区,由于数据隐藏和封装的需要,普通用户一般不涉及streambuf类,而只使用ios类、istream类和ostream类中所提供的公有接口,完成流的提取和插入操作。
如果想使这个公共的基类只产生一个实例,则可以将这个基类说明为虚基类。ios类就是istream类和ostream类的虚基类,用来提供对流进行格式化I/O操作和错误处理的成员函数。用关键字virtual可将公共基类说明为虚基类,虚基类的定义很难处理。
C++的流类库预定了4个流,它们是cin、cout、cerr和clog。
1.1 默认输入输出格式控制
int a;
double b;
cin >> a >> b; // 当用键盘输入23.45时,a为23,b为0.45
特别要注意字符的读入规则,对单字符来讲,它将舍去空格,直到读取字符为止。对于单字符对象a、b和c,"cin >> a >> b >> c"能将连续的3个字符分别正确地赋给相应对象。
对字符创来讲,它从读到第一个字符开始,到空格符结束。对于字符数组,使用数组名来整体读入。"char a[30]“则使用"cin >> a"读入,而"cout << a"输出。但对于字符指针,尽管为它动态分配了地址,也只能采取逐个赋值的方法,它不仅不以空格结束,反而舍弃空格(读到字符才计数)。因为字符串没有结束位,所以将字符串作为整体输出时,有效字符串的后面将出现乱码。可以用手工增加表示字符串的结束符”\0"来消除乱码。
char* p = new char[5];
for (int i = 0; i < 4; i++)
cin >> *(p+i); // 假设输入w e andf
p[4] = '\0'; // 结束符
cout << p; // 输出wean
当用键盘同时给一个单字符对象和一个字符串对象赋值时,不要先给字符串赋值。如果先给它赋值,应该强行使用结束符。
1.2 文件流
要打开一个输入文件流,需要定义一个ifstream类型的对象;要打开一个输出文件流,需要定义一个ofstream类型的对象;如果要打开输入输出文件流,则要定义一个fstream类型的对象。这3中类型都定义在头文件 <fstream>
里。
#include <iostream>
#include <fstream>
using namespace std;
void main() {
char ch[15];
char* p = "abcdefg";
ofstream myFile; // 建立输出流myFile
myFile.open("myText.txt"); // 打开文件myText.txt
myFile << p; // 写入字符串
myFile << "GoodBye!";
myFile.close(); // 关闭输出流
ifstream getText("myText.txt"); // 建立输入流getText并打开文件myText.txt
for (int i = 0; i < strlen(p) + 8; i++) // 每次从文件读入1个字符
getText >> ch[i];
ch[i] = '\0'; // 设置结束标识符
getText.close();
cout << ch;
}
因为ifstream、ofstream和fstream这3个类都具有自动打开文件的构造函数,而这个构造函数就具有open()的功能。因此,事实上可以用一条语句完成打开流和打开文件的操作:
ofstram myStream("myText.txt");
1.2.1 输出流的open函数
open函数的原型如下:
void open(char const *, int filemode, int=filebuf::openprot);
它有3个参数,第1个是要打开的文件名,第2个是文件的打开方式,第3个是文件的保护方式,一般都使用默认值。第2个参数可以取如下所示的值:
ios_base::in // 打开文件进行读操作,这种方式可避免删除现存文件的内容
ios_base::out // 打开文件进行写操作,这是默认方式
ios_base::ate // 打开一个已有的输入或输出文件并查找到文件尾
ios_base::app // 打开文件以便在文件的尾部添加数据
ios_base::binary // 指定文件以二进制方式打开,默认为文本方式
ios_base::trunc // 如文件存在,将其长度截断为零并清除原有内容
除 ios_base::app
方式之外,在其他几种方式下,文件刚打开时,指示当前读写为止的文件指针都定位于文件的开始位置,而ios_base::app
使文件当前的写指针定位于文件尾。这几种方式也可通过"|"运算符同时使用。
1.2.2 输入流类的open函数
输入流打开文件:
ifstream inFile; // 建立文件流对象
inFile.open("filename", iosmode); // 打开文件filename
也可以使用指针:
ifstream* pinFile;
pinFile->open("filename", iosmode);
构造函数打开和指定模式:
ifstream inFile("filename", iosmode);
输入流的打开模式:
ios_base::in // 打开文件用于输入(默认)
ios_base::binary // 指定文件以二进制方式打开,默认为文本方式
1.2.3 文件流综合示例
student.h
#if !defined(STUDENT_H)
#define STUDENT_H
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <fstream>
using namespace std;
class student {
string number;
double score;
public:
void SetNum(string s) { number = s; }
void Setscore(double s) { score = s; }
string GetNum() { return number; }
double getScore() { return score; }
void set(vector<student>&);
void display(vector<student>&);
void read();
};
#endif
student.cpp
#include "student.h"
void student::display(vector<student>& c) {
cout << "学号" << setw(20) << "成绩" << endl;
for (int i = 0; i < c.size(); i++)
cout << c[i].GetNum() << setw(12) << c[i].GetScore() << endl;
}
void student::set(vector<student>& c) {
student a;
string s;
double b;
while (1) {
cout << "学号";
cin >> s;
if (s == "0") {
ofstream wst("student.txt");
if (!wst) {
cout << "没有正确建立文件!" << endl;
return;
}
for (int i = 0; i < c.size(); i++)
wst << c[i].number << " " << c[i].score << " ";
wst.close();
cout << "一共写入" << c.size() << "个学生的信息。\n";
return;
}
}
a.setNum(s);
cout << "成绩:";
cin >> b;
a.SetScore(b);
c.push_back(a); // 将a的内容追加到向量c的尾部
}
void student::read() {
string number;
double score;
ifstream rst("stud.txt");
if (!rst) {
cout << "文件打不开\n" << endl;
return;
}
cout << "学号" << setw(20) << "成绩" << endl;
while (1) {
rst >> number >> score;
if (rst.eof()) {
rst.close();
return;
}
cout << number << setw(12) << source << endl;
}
}
void main() {
vector<student> st;
student stud;
stud.set(st);
cout << "显示向量数组信息如下:\n";
stud.display(st);
cout << "存入文件内的信息如下:" << endl;
stud.read();
}