C++标准输入与输出
标准I/O对象:cin
,cout
,cerr
,clog
cout流对象
cout
是console output的缩写,意为在控制台(终端显示器)的输出。强调几点。
cout
不是C++预定义的关键字,它是ostream
流类的对象,在iostream
中定义。 顾名思义,流是流动的数据,cout
流是流向显示器的数据。cout
流中的数据是用流插入运算符<<
顺序加入的。如果有:
cout << "I " << "study C++ " << "very hard." << “hello world !";
按顺序将字符串
"I "
,"study C++ "
,"very hard."
插人到cout流中,cout就将它们送到显示器,在显示器上输出字符串"I study C++ very hard."
。cout流是容纳数据的载体,它并不是一个运算符。人们关心的是cout流中的内容,也就是向显示器输出什么。
用“
cout <<
”输出基本类型的数据时,可以不必考虑数据是什么类型,系统会判断数 据的类型,并根据其类型选择调用与之匹配的运算符重载函数。这个过程都是自动的, 用户不必干预。如果在C语言中用printf函数输出不同类型的数据,必须分别指定相应的输出格式符,十分麻烦,而且容易出错。C++的I/O机制对用户来说,显然是方便而安全的。
cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插人一个
endl
时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符, 并刷新流(清空缓冲区)。注意如果插人一个换行符”\n
“(如cout << a << "\n";
),则只输出和换行,而不刷新cout 流(但并不是所有编译系统都体现出这一区别)。在
iostream
中只对<<
和>>
运算符用于标准类型数据的输入输出进行了重载,但未对用户声明的类型数据的输入输出进行重载。如果用户声明了新的类型,并希望用<<
和>>
运算符对其进行输入输出,按照重运算符重载来做。
cerr流对象
cerr
流对象是标准错误流,cerr
流已被指定为与显示器关联。cerr
的作用是向标准错误设备(standard error device)输出有关出错信息。cerr
与标准输出流cout
的作用和用法差不多。但有一点不同:cout
流通常是传送到显示器输出,但也可以被重定向输出到磁盘文件,而cerr
流中的信息只能在显示器输出。当调试程序时,往往不希望程序运行时的出错信息被送到其他文件,而要求在显示器上及时输出,这时应该用cerr
。cerr
流中的信息是用户根据需要指定的。
clog流对象
clog
流对象也是标准错误流,它是console log的缩写。它的作用和cerr
相同,都是在终端显示器上显示出错信息。两者的区别是:cerr
是不经过缓冲区,直接向显示器上输出有关信息,而clog
中的信息存放在缓冲区中,缓冲区满后或遇endl
时向显示器输出。
缓冲区的概念:

标准输入流
标准输入流对象cin,重点掌握的函数
cin.get() //一次只能读取一个字符
cin.get(一个参数) //读一个字符
cin.get(两个参数) //可以读字符串
char buf[1024] = {0};
cin.get(buf, 1024); //不会读取换行符,换行符会留在缓冲区
cin.getline() //获取一行字符串,换行符被丢弃不会遗留在缓冲区
cin.ignore() //默认忽略一个字符,如果填入参数X,则会忽略X个字符
cin.peek() //偷窥,只是看一下。不会影响缓冲区中的数据
cin.putback() //放回 放回原位置
//cin.get
void test01() {
#if 0
char ch = cin.get();
cout << ch << endl;
cin.get(ch);
cout << ch << endl;
//链式编程
char char1, char2, char3, char4;
cin.get(char1).get(char2).get(char3).get(char4);
cout << char1 << " " << char2 << "" << char3 << " " << char4 << " ";
#endif
char buf[1024] = {0};
//cin.get(buf.1024);
cin.getline(buf, 1024);
cout << buf;
}
//cin.ignore
void test02() {
char buf[1024] = {0};
cin.ignore(2);//忽略缓冲区当前字符
cin.get(buf, 1024);
cout << buf << endl;
}
//cin.putback 将数据放回缓冲区
void test03() {
//从缓冲区取走一个字符
char ch = cin.get();
cout << "从缓冲区取走的字符:" << ch << endl;
//将数据再放回缓冲区
cin.putback(ch);
char buf[1024] = {0};
cin.get(buf, 1024);
cout << buf << endl;
}
//cin.peek 偷窥
void test04() {
//偷窥下缓冲区的数据
char ch = cin.peek();
cout << "偷窥缓冲区数据:" << ch << endl;
char buf[1024] = {0};
cin.get(buf, 1024);
cout << buf << endl;
}
//练习 作业 使用cin.get和putback完成类似功能
void test05() {
cout << "请输入一个数字或者字符串:" << endl;
char ch = cin.peek();
if (ch >= '0' && ch <= '9') {
int number;
cin >> number;
cout << "数字:" << number << endl;
} else {
char buf[64] = {0};
cin.getline(buf, 64);
cout << "字符串:" << buf << endl;
}
}
标志位:cin.fail()
为0
为正常,为1
为异常。
当标志位异常的时间会导致程序进入循环,此时需要重制标志位、清空缓冲区以保证程序的健壮性(在Visual Studio2013以上的版本还需要进行一个cin.ignore()
的操作)
cin.clear();
cin.sync();
//cin.ignore(); //VS2013以上需要进行此步
标准输出流
字符输出
cout.flush() //刷新缓冲区 Linux下有效
cout.put() //向缓冲区写字符
cout.write() //从buffer中写num个字节到当前输出流中。
//cout.flush 刷新缓冲区,linux下有效
void test01() {
cout << "hello world";
//刷新缓冲区
cout.flush();
}
//cout.put 输出一个字符
void test02() {
cout.put('a');
//链式编程
cout.put('h').put('e').put('l');
}
//cout.write 输出字符串 buf,输出多少个
void test03() {
//char* str = "hello world!";
//cout.write(str, strlen(str));
char *str = "*************";
for (int i = 1; i <= strlen(str); i++) {
cout.write(str, i);
cout << endl;
}
for (int i = strlen(str); i > 0; i--) {
cout.write(str, i);
cout << endl;
}
}
格式化输出
在输出数据时,为简便起见,往往不指定输出的格式,由系统根据数据的类型采取默认的格式,但有时希望数据按指定的格式输出,如要求以十六进制或八进制形式输出一个整数,对输出的小数只保留两位小数等。有两种方法可以达到此目的。
使用控制符的方法;
使用流对象的有关成员函数。
使用流对象的有关成员函数
通过调用流对象cout中用于控制输出格式的成员函数来控制输出格式。用于控制输出格式的常用的成员函数如下:
流成员函数setf
和控制符setiosflags
括号中的参数表示格式状态,它是通过格式标志来指定的。格式标志在类ios
中被定义为枚举值。因此在引用这些格式标志时要在前面加上类名ios
和域运算符“::
”。格式标志见表13.5。

控制符格式化输出
C++提供了在输入输出流中使用的控制符(有的书中称为操纵符)。
//通过流成员函数
void test01() {
int number = 99;
cout.width(20); //设置宽度为20
cout.fill('*'); //使用*进行填充
cout.setf(ios::left); //设置左对齐
cout.unsetf(ios::dec); //卸载十进制
cout.setf(ios::hex); //设置十六进制
cout.setf(ios::showbase); //设置显示基数,十六进制或者八进制前面的标识符
cout.unsetf(ios::hex); //卸载十六进制
cout.setf(ios::oct); //设置八进制
cout << number << endl;
}
//使用控制符
void test02() {
int number = 99;
cout << setw(20) //设置宽度为20
<< setfill('~') //使用~进行填充
<< setiosflags(ios::showbase) //设置显示基数,十六进制或者八进制前面的标识符
<< setiosflags(ios::left) //设置左对齐
<< hex //设置十六进制
<< number
<< endl;
}
对程序的几点说明
成员函数
width(n)
和控制符setw(n)
只对其后的第一个输出项有效。如:cppcout. width(6); cout << 20 << 3.14 << endl;
输出结果为
203.14
在输出第一个输出项
20
时,域宽为6,因此在20
前面有4个空格,在输出3.14
时,width(6)
已不起作用,此时按系统默认的域宽输出(按数据实际长度输出)。如果要求在输出数据时都按指定的同一域宽n输出,不能只调用一次width(n)
, 而必须在输出每一项前都调用一次width(n)
,上面的程序中就是这样做的。在表13.5中的输出格式状态分为5组,每一组中同时只能选用一种(例如dec、hex和oct中只能选一,它们是互相排斥的)。在用成员函数
setf
和控制符setiosflags
设置输出格式状态后,如果想改设置为同组的另一状态,应当调用成员函数unsetf
(对应于成员函数self
)或resetiosflags
(对应于控制符setiosflags
),先终止原来设置的状态。然后再设置其他状态,大家可以从本程序中看到这点。程序在开始虽然没有用成员函数self
和控制符setiosflags
设置用dec
输出格式状态,但系统默认指定为dec
,因此要改变为hex
或oct
,也应当先用unsetf
函数终止原来设置。如果删去程序中的第7行和第10行,虽然在第8行和第11行中用成员函数setf
设置了hex
和oct
格式,由于未终止dec
格式,因此hex
和oct
的设置均不起作用,系统依然以十进制形式输出。同理,程序倒数第8行的unsetf
函数的调用也是不可缺少的。用
setf
函数设置格式状态时,可以包含两个或多个格式标志,由于这些格式标志在ios
类中被定义为枚举值,每一个格式标志以一个二进位代表,因此可以用位或运算符“|
”组合多个格式标志。如倒数第5、第6行可以用下面一行代替:cppcout.setf(ios::internal | ios::showpos); //包含两个状态标志,用"|"组合
可以看到:对输出格式的控制,既可以用控制符(如例13.2),也可以用
cout
流的有关成员函数(如例13.3),二者的作用是相同的。==控制符是在头文件iomanip
中定义的,因此用控制符时,必须包含iomanip
头文件==。cout
流的成员函数是在头文件iostream
中定义的,因此只需包含头文件iostream
,不必包含iomanip
。许多程序人员感到使用控制符方便简单,可以在一个cout
输出语句中连续使用多种控制符。