C++_IO与文件2-用cout进行输出

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<<"\n";

 

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:\n";
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:\n";
24     for(i = len; i>0; i--)
25         cout.write(state2, i) <<endl;
26 
27 //exceed string length
28     cout<<"Exceeding string length:\n";
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\n";
12 
13 //set to hex mode
14     cout<<hex;
15     cout<<n << "   ";
16     cout<<n*n<<" (hexdecimal)\n";
17 
18 //set to octal mode
19     cout<<oct<<n<<"   "<<n*n<<" (octal)\n";
20 
21 //alternative way to call a manipulator
22     dec(cout);
23     cout<<n<<"     "<<n*n<<" (decimal)\n";
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<<"#\n";

  显示结果:

    #          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 <<" : \n";
 9     cout.width(5);
10     cout<<"N"<<' : ';
11     cout.width(8);
12     cout<<"N*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<<":\n";
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]<<"\n";
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<<"!\n";
11     cout<<"\"Fiery Friends\" is $"<<price2<<"!\n";
12     
13     cout.precision(2)
14     cout<<"\"Fiery Friends\" is $"<<price1<<"!\n";
15     cout<<"\"Fiery Friends\" is $"<<price2<<"!\n";
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<<"!\n";
14     cout<<"\"Fiery Friends\" is $"<<price2<<"!\n";
15     
16     cout.precision(2)
17     cout<<"\"Fiery Friends\" is $"<<price1<<"!\n";
18     cout<<"\"Fiery Friends\" is $"<<price2<<"!\n";
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\n";
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\n";
20     cout<<temperature<<endl;
21     cout<<"How "<<true<<"!   oops  -- How ";
22     cout.setf(ios_base::boolappha);
23     cout<<true<<<<"!\n";
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\n";
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 }

 

posted @ 2019-03-08 00:50  Grooovvve  阅读(577)  评论(0)    收藏  举报