C++将输出流看作是字节流,在程序中,很多数据被组织成比字节更大的单位。
例如int类型由16位或者32位的二进制值表示;double值由64位的二进制数据表示;
但是在将字节流发送给屏幕时,希望每个字节表示一个字符。
举个例子:要在屏幕上显示数字-2.34时,需要5个字符(- 2 . 3 4)。
而不是这个值得内部浮点表示发送到屏幕上。
ostream类最重要的任务就是将数值类型转换为以文本形式表示的字符流。
ostream类将数据的内部表示(二进制位模式)转换为由字符字节组成的输出流。
//输出流是字节流,所以要显示的东西必须进行转换,转换成字符编码的字节流;
=====================================================
一、重载的<<运算符
ostream类重新定义了<<运算符,方法是将其重载为输出。该重载运算符能够识别C++中所有的基本类型;
<<运算符的默认含义是按位左移运算符。
例如:cout<<88对应于下面的方法原型:
ostream & operator<<(int);
operator<<(int)函数接受一个int参数,返回一个指向ostream对象的引用,这使得可以将输出连接起来,如下所示:
cout<<"I'm feeling sedimental over "<<boundary<<" ";
1、输出和指针
ostream类为下面的指针类型提供了插入运算符函数:
const signed char *
const unsigned char *
const char *
void *
C++用指向字符串存储位置的指针来表示字符串。指针的形式可以是char数组名、显式的char指针或用引号括起的字符串。
方法使用字符串中的终止空字符来确定何时停止显示字符。
void * 指的是其他类型的指针,会打印地址的数值表示;如果要获得字符串的地址,则必须将其强制转换为其他类型:
char * amount = "dozen";
cout<<amount; //print the string "dozen"
cout<<(void *)amount; //prints the address of the "dozen" string
2、拼接输出
插入运算符的所有返回为ostream &,也就是说,原型的格式如下:
ostream & operator<<(type);
type是要显示的数据类型,ostream &意味着将返回一个指向ostream对象的引用。
指向哪个对象呢?函数定义指出,引用将指向用于调用该运算符的对象。
换句话说,运算符函数的返回值为调用运算符的对象。
例如:cout<<"potluck"返回的是cout对象。这种特性使得能够通过插入运算符来连接输出。
=====================================================
二、其他ostream方法
ostream类还提供了put()方法和write()方法;前者显示字符、后者用于显示字符串;
可以用类方法表示法来调用put:
cout.put('W'); //display the W character
cout是调用对象的方法、put是类成员函数。该函数也返回一个指向调用对象的引用,因此可以用它拼接输出;
write()方法显示整个字符串,其模板原型如下:
basic_ostream<char T, traits> & write(const char_type* s, streamsize n);
接下来看一个例子:
1 //write.cpp -- using cout.write() 2 #include <iostream> 3 #include <cstring> 4 5 int main() 6 { 7 using std::cout; 8 using std::endl; 9 const char * state1 = "Florida"; 10 const char * state2 = "Kansas"; 11 const char * state3 = "Euphoria"; 12 13 int len = std:strlen(state2); 14 cout<<"Increasing loop index: "; 15 int i; 16 for (i = 1; i<=len; i++) 17 { 18 cout.write(state2, i); 19 cout<<endl; 20 } 21 22 //concatenate output 23 cout<<"Decreasing loop index: "; 24 for(i = len; i>0; i--) 25 cout.write(state2, i) <<endl; 26 27 //exceed string length 28 cout<<"Exceeding string length: "; 29 cout.write(state2, len+5)<<endl; 30 31 return 0; 32 }
程序输出结果:
Increasing loop index:
K
Ka
Kan
Kans
Kansa
Kansas
Decreasing loop index:
Kansas
Kansa
Kans
Kan
Ka
K
另外,write()方法也可以用于数值数据,可以将数字的地址强制转换为char *,然后传递给它:
long val = 560031841;
cout.write((char *) & val, sizeof(long));
这不会将数字转换为相应的字符,而是传输内存中存储的位表示。
例如,4字节的long值(560031841)将作为4个独立的字节被传输。输出设备将把每个字节作为ASCII码进行解释。
因此在屏幕上,560031841将被显示为4个字符的组合,这很可能是乱码。
write()确实为将数值数据存储在文件中提供了一种简洁、准确的方式。
=====================================================
三、刷新输出缓冲区
缓冲区一般为512个字节或者其整数倍。
输出不会立即发送到目标地址,而是被存储在缓冲区中,直到缓冲区填满。
然后程序将刷新(flush)缓冲区,把内容发送出去,并清空缓冲区,以填充新的数据。
但是对于屏幕输出而言,填充缓冲区的重要性低很多。
所幸,在屏幕输出时,程序不必等到缓冲区被填满后才刷新缓冲区;
当换行符发送到缓冲区后,将刷新缓冲区。
cout<<"Hello, good-looking! "<<flush;
cout<<"Wait just a moment, please"<<endl;
控制符flush刷新缓冲区,而控制符endl也刷新缓冲区;
cout<<"Enter a number:";
float num;
cin>>num;
上面这段代码,没有刷新缓冲区,从而显示Enter a number: 字符,那样的话用户也就无法从屏幕感知到这个提示语来进行输入;
=====================================================
四、用cout进行格式化
1、修改显示时使用的计数系统
可以使用:hex(cout) 表示下面的函数调用将cout对象的计数系统格式状态设置为十六进制。
但通常的使用方法为:cout<<hex;
1 //manip.cpp -- using format manipulators 2 #include <iostream> 3 4 int main() 5 { 6 using namespace std; 7 cout<<"Enter an integer: "; 8 int n; 9 cin>> n; 10 11 cout<<"n n*n "; 12 13 //set to hex mode 14 cout<<hex; 15 cout<<n << " "; 16 cout<<n*n<<" (hexdecimal) "; 17 18 //set to octal mode 19 cout<<oct<<n<<" "<<n*n<<" (octal) "; 20 21 //alternative way to call a manipulator 22 dec(cout); 23 cout<<n<<" "<<n*n<<" (decimal) "; 24 25 return 0; 26 }
运行结果为:
Enter an integer:13
n n*n
13 169(decimal)
d a9 (hexadecimal)
15 251(octal)
13 169(decimal)
2、调整字段宽度
先看一个例子:
cout<<'#';
cout.width(12);
cout<<12<<"#"<< 24<<"# ";
显示结果:
# 12#24#
我们可以发现第一个12被设置为宽度12的字段,12这个字符被放到最右边,这被称为右对齐。
然后字段宽度恢复为默认值。
所以可知width()方法只影响接下来显示的一个项目,然后字段宽度将恢复为默认值。
另外注意一点,C++永远不会截短数据,因此如果试图在宽度为2的字段中打印一个7位值,C++将增宽字段,以容纳该数据。
C/C++的原则是显示所有数据比保持列的整洁更重要。
1 //width.cpp -- using the width method 2 #include <iostream> 3 4 int main() 5 { 6 using std :: cout; 7 int w = cout.width(30); 8 cout<<"default field width =" << w <<" : "; 9 cout.width(5); 10 cout<<"N"<<' : '; 11 cout.width(8); 12 cout<<"N*N"<<": "; 13 14 for (long i = 1; i<=100; i*=10) 15 { 16 cout.width(5); 17 cout<<i<<' : '; 18 cout.width(8); 19 cout<<i * i<<": "; 20 } 21 22 return 0; 23 }
运行结果:
default field width = 0;
N: N * N:
1: 1:
10: 100:
100: 10000:
空格被插入到值的左侧,用来填充的字符叫做填充字符。右对齐是默认的。
默认字段宽度是0,因为C++总会增长字段,以容纳数据,因此这种值适用于所有数据。
3、填充字符
在默认情况下,cout用空格填充字段中未被使用的部分,可以用fill()成员函数来改变填充字符。
下面的函数调用将填充字符改为星号:
cout.fill(' * ');
注意,与字段宽度不同的是,新的填充字符将一直有效,直到更改它为止;
1 //fill.cpp -- changing fill character for fields 2 #include<iostream> 3 4 int main() 5 { 6 using std::cout; 7 cout.fill('*'); 8 const char * staff[2] = {"Waldo Whipsnade", "Wilmarie Wooper"}; 9 long bonus[2] = {900, 1350}; 10 11 for (int i = 0; i < 2; i++) 12 { 13 cout<<staff[i]<<" : $"; 14 cout.width(7); 15 cout<<bonus[i]<<" "; 16 } 17 return 0; 18 }
运行结果:
Waldo Whipsnade: $****900
Wilmarie Wooper: $***1350
4、设置浮点数的显示精度
浮点数精度的含义取决于输出模式;
在默认模式下,它指的是显示的总位数;
在定点模式和科学模式下,精度指的是小数点后面的位数。
C++的默认精度为6位(但末尾的0将不显示)。
precision()成员函数使得能够选择其他值。
下面的语句将cout的精度设置为2:cout.precision(2)
与fill()类似,新的精度设置将一直有效,直到被重新设置。
1 //precise.cpp -- setting the precision 2 #include<iostream> 3 4 int main() 5 { 6 using std::cout; 7 float price1 = 20.40; 8 float price2 = 1.9 + 8.0/9.0; 9 10 cout<<""Fiery Friends" is $"<<price1<<"! "; 11 cout<<""Fiery Friends" is $"<<price2<<"! "; 12 13 cout.precision(2) 14 cout<<""Fiery Friends" is $"<<price1<<"! "; 15 cout<<""Fiery Friends" is $"<<price2<<"! "; 16 17 return 0; 18 }
运行结果:
"Furry Friends" is 20.4!
"Furry Friends" is 2.78889!
"Furry Friends" is 20!
"Furry Friends" is 2.8!
5、打印末尾的0和小数点
对于有些输出,保留末尾的0将更为美观。
但ios_base类提供了一个setf()函数(用于set标记),能够控制多种格式化特性。
这个类还定义了很多常量,可用作该函数的参数;
例如,下面的函数调用使cout显示末尾小数点:
cout.setf(ios_base :: showpoint);
使用默认的浮点格式时,上述语句还将导致末尾的0倍显示出来。
如果使用默认精度(6位)时,cout不会将2.00显示为2,而是将它显示为2.000000。
1 //precise.cpp -- setting the precision 2 #include<iostream> 3 4 int main() 5 { 6 using std::cout; 7 using std::ios_base; 8 9 float price1 = 20.40; 10 float price2 = 1.9 + 8.0/9.0; 11 12 cout.setf(ios_base::showpoints); 13 cout<<""Fiery Friends" is $"<<price1<<"! "; 14 cout<<""Fiery Friends" is $"<<price2<<"! "; 15 16 cout.precision(2) 17 cout<<""Fiery Friends" is $"<<price1<<"! "; 18 cout<<""Fiery Friends" is $"<<price2<<"! "; 19 20 return 0; 21 }
运行结果:
"Fiery Friends" is $20.4000!
"Fiery Friends" is $2.78889!
"Fiery Friends" is $20.!
"Fiery Friends" is $2.8!
6、再谈setf()
ios_base类定义了代表位值的常量;//由于这些格式常量都在ios_base类中定义。因此使用它们时,必须加上作用域解析运算符;
ios_base : : boolalpha 输入和输出bool值,可以为true或false
ios_base : : showbase 对于输出,使用C++基数前缀(0, 0x)
ios_base : : showpoint 显示末尾的小数点
ios_base : : uppercase 对于16进制输出,使用大写字母,E表示法
ios_base : : showpos 在整数前面加上+
setf()函数由两个原型。第一个为:
fmtflags setf(fmtflags);
fmtflags 是bitmask类型的typedef名,用于存储格式标记。
bitmask类型是用来存储各个位值的类型。
1 //setf.cpp -- using setf() to control formatting 2 #include<iostream> 3 4 int main() 5 { 6 using std::cout; 7 using std::endl; 8 using std::ios_base; 9 10 int temperature = 63; 11 cout<<"Today's water temperature: "; 12 cout.setf(ios_base :: showpos); //show plus signal 13 cout<< temperature << endl; 14 15 cout<<"For our programming friends, that's "; 16 cout<< std::hex << temperature << endl; //use hex 17 cout.setf(ios_base::uppercase); //use uppercase in hex 18 cout.setf(ios_base::showbase); //use 0X prefix for hex 19 cout<<"or "; 20 cout<<temperature<<endl; 21 cout<<"How "<<true<<"! oops -- How "; 22 cout.setf(ios_base::boolappha); 23 cout<<true<<<<"! "; 24 25 return 0; 26 }
第二个setf()原型接受两个参数,并返回以前的设置:
fmtflags setf(fmtflags, fmtflags);
函数的这种重载格式用于设置由多位控制的格式选项。
第一个参数和以前一样,也是一个包含了所需设置的fmtflags值。
第二个参数指出要清除第一个参数中的哪些位。
setf(long, long)的参数
第二个参数 第一个参数 含义
ios_base::basefield ios_base::dec 使用基数10
ios_base::oct 使用基数8
ios_base::hex 使用基数16
ios_base::floatfield ios_base::fixed 使用定点计数法
ios_base::scientific 使用科学计数法
ios_base::adjustfield ios_base::left 使用左对齐
ios_base::right 使用右对齐
ios_base::internal 符号或基数前缀
7、标准控制符
使用setf()不是进行格式化的,对用户最友好的方法;
C++提供了多个控制符,能够调用setf(),并自动提供正确的参数;
一些标准控制符
控制符 调用
boolalpha setf(ios_base::boolalpha)
noboolalpha unsetf(ios_base::noboolalpha)
showbase setf(ios_base::showbase)
noshowbase unsetf(ios_base::showbase)
等等。。。
8、头文件iomanip
使用iomanip工具来设置一些格式值不太方便。
为了简化工作,C++在头文件iomanip中提供了其他一些控制符,它们能够提供前面讨论过的服务,但表示起来更方便。
3个最常用的控制符分别是:setprecision()、setfill()、setw();
它们分别用来设置精度、填充字符、字段宽度;
1 //iomanio.cpp -- using manipulators from iomanip 2 #include<iostream> 3 #include<iomanip> 4 #include<cmath> 5 6 int main() 7 { 8 using namespace std; 9 cout<<fixed<<right; 10 11 cout<<setw(6)<<"N"<<setw(14)<<"square root"<<setw(15)<<"fourth root "; 12 13 double root; 14 for(int n =10; n<=100; n+=10) 15 { 16 root = sqrt(double(n)); 17 cout<<setw(6)<<setfill('.')<<n<<setfill(' ') 18 <<setw(12)<<setprecision(3)<<root 19 <<setw(14)<<setprecision(4)<<sqrt(root) 20 <<endl; 21 } 22 23 return 0; 24 }