龙空技术网

C++ 借助cout进行控制台文本输出的基本原理及复杂格式控制方法

海洋饼干叔叔 537

前言:

目前朋友们对“c语言中的input怎么用”都比较注重,朋友们都需要剖析一些“c语言中的input怎么用”的相关文章。那么小编也在网络上汇集了一些有关“c语言中的input怎么用””的相关文章,希望各位老铁们能喜欢,姐妹们快快来了解一下吧!

建议老师们将本文收藏,待学生的需要时发给学生,免费口舌。

在C++里,我们使用cout进行控制台文本输出。这在学习编程的阶段很常用,但在真实的工作场合却极少使用,毕竟大部分的应用程序都是基于图形界面,而不是终端的。甚至,在C/C++的某些应用场合,比如单片机编程里,嵌入式设备甚至连屏幕都没有。

考虑到部分OJ系统中的在线编程题可能对输出格式作出精细要求,这里我们对cout控制输出进行“详细”讨论:包括cout的基本工作原理,以及通过cout进行精细格式输出的方法。

对工作原理不感兴趣,只想快速知道HOW的有编程经验的读者请直接阅读本文的第6部分。

知识产权协议

允许以教育/培训为目的向学生或受众进行免费引用,展示或者讲述,无须取得作者同意。

不允许以电子/纸质出版为目的进行摘抄或改编。

1. cout基本工作原理

下述代码可以帮助我们理解通过cout的插入操作符(insertion operator<<)进行控制台文本输出的基本原理。

//Project - COUT#include <iostream>using namespace std;int main() {    cout << "pi = " << 3.14159 << endl;    operator<<(cout,"pi = ").operator<<(3.14159).operator<<(endl);    return 0;}

上述代码的执行结果为:

pi = 3.14159pi = 3.14159

在iostream头文件中,很容易找到cout的定义。cout是一个类型为ostream的对象,其被连接到了标准输出流,即控制台。

extern ostream cout;    /// Linked to standard output

上述程序的第6行与第7行完全等价。相关代码的执行过程如下:

cout << “pi = “的实质是执行了一个名为operator<<( )的函数,其中,第1个参数是cout对象,第2个参数是”pi = “。这个函数把第2个参数的字符串输出到第1个参数所代表的控制台中。同时,该函数返回了cout的引用作为函数执行的结果。… << 3.141519则以前述函数调用所返回的cout引用作为基础,执行其成员函数operator<<( )。该成员函数存在多个函数名重载的版本,其中一个版本接受一个double作为参数,并将double的值输出到cout。同样地,本次函数调用也返回了cout的引用作为结果。类似地,… << endl同样对应cout.operator<<( )函数的一次执行,该成员函数的一个重载版本接受endl作为参数,并向cout所代表的控制台输出一个换行符。

<<操作符在C语言里用作左移位操作,C++的标准模板库通过定义与该操作符“同名”的函数,扩展了该操作符的功能:向cout输出对象内容。

总结:iostream定义了多个重载的operator<<()操作符函数,这些不同版本的函数接受不同类型的参数,包括int, char, float, double, const char*, string等,并将这些参数对象转换成字符串,并输出到控制台屏幕上。

2. 改变进制

//Project - HexOct#include <iostream>#include <stdio.h>using namespace std;int main(){    int b = 0x17;          //十六进制 hexadecimal    int c = 017;           //八进制   octal    int d = 0b01111110;    //二进制   binary    cout << "0x17 = " << b << ", 017 = " << c << ", 0b01111110 = " << d << endl;    printf("17: %x, %d, %o\n", 17, 17, 17);    cout << "17: " << hex << 17 << ", " << dec << 17 << ", " << oct << 17 << endl;    hex(cout);    cout << "17: " << 17 << ", ";    dec(cout);    cout << 17 << ", ";    oct(cout);    cout << 17 << endl;    return 0;}

上述代码的执行结果为:

0x17 = 23, 017 = 15, 0b01111110 = 12617: 11, 17, 2117: 11, 17, 2117: 11, 17, 21

上述代码可以看出,通过执行cout << hex,可以改变cout的内部状态,使用在后续输出数值时使用16进制。cout << dec (十进制),cout << oct (八进制)同理。

事实上,这里的hex, dec, oct是一种被称之为操作算子(manipulator)的特殊函数。下述3行代码事实上等价:

cout << hex;cout.operator<<(hex);hex(cout);

在ios_base.h中我们可以找到hex()函数的定义:

inline ios_base&hex(ios_base& __base){  __base.setf(ios_base::hex, ios_base::basefield);  return __base;}

在形式上,cout << hex被解释成多轮函数调用,首先是:

cout.operator<<(hex);

这个被重载的operator<<()函数将hex函数名当成一个函数指针,然后通过这个函数指针调用hex()函数:

hex(cout);

而hex()函数又通过cout的setf()函数发挥作用。读者或许会疑惑说,这么多层的函数调用是否会降低代码的执行效率,事实上,由于相关函数多是内联(inline)的,编译器的优化会消除这些“形式”上的不必要的函数调用。

3. 输出宽度控制

#include <iostream>#include <cmath>using namespace std;int main(){    cout << "12345678901234567890" << endl;    cout << "--------------------" << endl;    cout.width(5);    cout << "N";    cout.width(15);    cout << "2**N" << endl;    cout << "--------------------" << endl;    for (auto n=0;n<=10;n++){        cout.width(5);        cout << n;        cout.width(15);        cout << pow(2,n) << endl;    }    return 0;}

上述代码的执行结果为:

12345678901234567890--------------------    N           2**N--------------------    0              1    1              2    2              4    3              8    4             16    5             32    6             64    7            128    8            256    9            512   10           1024

cout的成员函数width( )可以设定下一个输出项的宽度,但其作用范围仅限后一个输出项。当输出项的实际宽度小于设定宽度时,其左侧以空格填充。可以看到,上述程序借助于width( )函数,输出了一个严格右对齐的表格,第一列的宽度为5,第2列的宽度为15。

通过fill( )成员函数,可以修改填充字符。请参见下述程序及其执行结果。

#include <iostream>using namespace std;int main(){    cout.fill('*');  //修改填充字符为*    cout.width(5);    cout << "idx";    cout.width(15);    cout << "content";    return 0;}

上述程序的执行结果为:

**idx********content
4. 浮点数的输出格式

通过cout的precision( )函数可以设置其输出浮点数时的精度。见下述代码。

#include <iostream>using namespace std;int main(){    float v1 = 17.90f;    float v2 = 3.1415926535798932f;    cout << "before .precision(2)" << endl;    cout << "v1 = " << v1 << endl;    cout << "v2 = " << v2 << endl;    cout.precision(2);    cout << "after .precision(2)" << endl;    cout << "v1 = " << v1 << endl;    cout << "v2 = " << v2 << endl;    return 0;}

执行结果为:

before .precision(2)v1 = 17.9v2 = 3.14159after .precision(2)v1 = 18v2 = 3.1

上述执行结果与我们的期望有很大不同。

在默认情况下,cout输出浮点数的精度为6,且这个精度并不是指小数点后的位数,而是所有的位数。cout会借助于四舍五入的方法输出指定“精度”的字符串,同时会舍弃末尾多余的0。

执行结果的第2行显示,17.90被舍弃掉末尾的0,输出为17.9。

执行结果的第3行显示,3.1415926535798932被四舍五入为3.14159,正好6位数字。

接下来,cout.precision(2)设定浮点数输出精度为2,此时,17.90被四舍五入输出为18,3.1415926535798932被四舍五入输出为3.1,都是两位数字。

如果期望cout输出的浮点数的位数确定,当位数不足时用0补齐,可以执行cout.setf(ios_base::showpoint)。

#include <iostream>using namespace std;int main(){    float v1 = 17.90f;    float v2 = 3.1415926535798932f;    cout.setf(ios_base::showpoint);    cout.precision(4);    cout << "v1 = " << v1 << endl;    cout << "v2 = " << v2 << endl;    return 0;}

执行结果为:

v1 = 17.90v2 = 3.142

多数人更愿意”精度“被定义为小数点后的位数。

cout.setf(ios_base::floatfield, ios_base::fixed)将cout设置为定点小数模式,在该模式下,”精度”表示小数点后的位数,见下述程序。

#include <iostream>using namespace std;int main(){    float v1 = 17.90f;    float v2 = 3.1415926535798932f;    cout.precision(4);    cout.setf(ios_base::floatfield,ios_base::fixed);    cout << "v1 = " << v1 << endl;    cout << "v2 = " << v2 << endl;    return 0;}

执行结果为:

v1 = 17.9000v2 = 3.1416

cout.setf(ios_base::floatfield, ios_base::scientific)将cout设置为科学计数法模式,在该模式下,“精度”也表示小数点后的位数,见下述程序。

#include <iostream>using namespace std;int main(){    float v1 = 17.90f;    float v2 = 3.1415926535798932f;    cout.precision(4);    cout.setf(ios_base::floatfield,ios_base::scientific);    cout << "v1 = " << v1 << endl;    cout << "v2 = " << v2 << endl;    return 0;}

执行结果为:

v1 = 1.7900e+001v2 = 3.1416e+000
5. 应用setf( )进行格式控制

cout的setf( )成员函数有两个原型。

原型1:fmtflags setf(fmtflags)

fmtflags常量

用途

ios_base::boolalpha

将bool型输出为true或flase

ios_base::showbase

显示进制(八进制 - 0, 十六进制 - 0x)

ios_base::showpoint

总显示小数点,位数不足时补0

ios_base::uppercase

对16进制输出或者科学计数法中的E使用大写字母

ios_base::showpos

为正数输出显示+号

请结合下述代码及其执行结果来理解上述fmtflags常量的用途。

#include <iostream>using namespace std;int main(){    int v = 17;    cout << "true = " << true << endl; //true被输出为"1"    cout.setf(ios_base::boolalpha);    //按"true"/"false"输出布尔型    cout << "false = " << false << endl; //false被输出为"false"    cout.setf(ios_base::showpos);  //正数显示+号,但通常只在10进制时有效    cout << "v1 = " << v << endl;    cout << "v2 = " << hex << v << endl;    cout.setf(ios_base::showbase); //显示0,0x等进制符号    cout.setf(ios_base::uppercase);//相关字母大写    cout << "v3 = " << hex << v << endl;    return 0;}

上述代码的执行结果为:

true = 1            //ios_base::boolalpha设置前,true被输出为1false = false       //ios_base::boolalpha设置后,false被输出为falsev1 = +17            //ios_base::showpos导致十进制正数前出现+号v2 = 11             //16进制输出v3 = 0X11           //ios_base::showbase导致0x的添加,ios_base::uppercase使得0x变成0X

对于8进制和16进制,ios_base::showpos通常无效,因为在多数的应用场景下,人们只会使用8进制或者16进制来表达无符号的整数,而无符号整数,是没有正负概念的。

在完成某项格式设置之后,如果期望还原该项设置,可以使用unsetf( )函数。举例如下。

#include <iostream>using namespace std;int main(){    int v = 17;    cout.setf(ios_base::showpos);    cout << v << endl;    cout.unsetf(ios_base::showpos);    cout << v << endl;    return 0;}

上述代码的执行结果为:

+1717

原型2:fmtflags setf(fmtflags, fmtflags)

参数1

参数2

用途

ios_base::dec

ios_base::basefield

使用10进制

ios_base::oct

使用8进制

ios_base::hex

使用16进制

ios_base::fixed

ios_base::floatfield

使用定点小数格式

ios_base::scientific

使用科学计数法

ios_base::left

ios_base::adjustfield

左对齐

ios_base::right

右对齐

ios_base::internal

符号(+, -, 0, 0x等)左对齐,值右对齐

下述代码及其执行结果演示了通过setf( )函数的第2个原型来设置输出内容左/右对齐的方法。

#include <iostream>#include <cmath>using namespace std;int main(){    cout << "12345678901234567890" << endl;    cout << "--------------------" << endl;    cout.width(5);    cout.setf(ios_base::left,ios_base::adjustfield);    cout << "N";    cout.width(15);    cout.setf(ios_base::right,ios_base::adjustfield);    cout << "2**N" << endl;    cout << "--------------------" << endl;    cout.setf(ios_base::showpos);    for (auto n=-2;n<=10;n++){        cout.width(5);        cout.setf(ios_base::left,ios_base::adjustfield);        cout << n;        cout.width(15);        cout.setf(ios_base::right,ios_base::adjustfield);        cout << pow(2,n) << endl;    }    return 0;}

上述代码的执行结果为:

12345678901234567890--------------------N               2**N---------------------2             +0.25-1              +0.5+0                +1+1                +2+2                +4+3                +8+4               +16+5               +32+6               +64+7              +128+8              +256+9              +512+10            +1024

从执行结果可见,上述表格的第1列为左对齐,第2列则为右对齐。

6. 通过操作算子(manipulator)简化格式设置

下述代码的第5行与第6~7行效果相同,它们都按照16进制格式输出17,结果为11。

#include <iostream>using namespace std;int main(){    cout << hex << 17 << endl;    cout.setf(ios_base::hex,ios_base::floatfield);    cout << 17 << endl;    return 0;}

上述代码的执行结果为:

1111

如本文的第2部分所述,hex事实上是一个被称为操作算子(manipulator)的函数,该函数事实上通过调用setf(ios_base::hex, ios_base::floatfield)发挥作用。显然,操作算子更加用户友好。

下表介绍了常用的操作算子。

操作算子

等价setf( )调用

用途

cout << boolalpha

setf(ios_base::boolalpha)

用true/false表达布尔型

noboolalpha

unsetf(ios_base::boolalpha)

还原前项设置

showbase

setf(ios_base::showbase)

使用0/0x表达8/16进制

noshowbase

unsetf(ios_base::showbase)

还原前项设置

showpoint

setf(ios_base::showpoint)

总显示小数点,位数不足时补0

noshowpoint

unsetf(ios_base::showpoint)

还原前项设置

showpos

setf(ios_base::showpos)

在10进制正数前显示+号

noshowpos

unsetf(ios_base::showpos)

还原前项设置

uppercase

setf(ios_base::uppercase)

对16进制输出或者科学计数法中的E使用大写字母

nouppercase

unsetf(ios_base::uppercase)

还原前项设置

internal

setf(ios_base::internal,ios_base::adjustfield)

符号(+, -, 0, 0x等)左对齐,值右对齐

left

setf(ios_base::left,ios_base::adjustfield)

左对齐

right

setf(ios_base::right,ios_base::adjustfield)

右对齐

oct

setf(ios_base::oct,ios_base::basefield)

8进制

dec

setf(ios_base::dec,ios_base::basefield)

10进制

hex

setf(ios_base::hex,ios_base::basefield)

16进制

fixed

setf(ios_base::fixed,ios_base::floatfield)

定点小数

scientific

setf(ios_base::scientific,ios_base::floatfield)

科学计数法

此外,通过头文件iomanip还可以引入另外几个重要的操作算子。iomanip是input & output manipulator的缩写。

操作算子

等效于

用途

setw(n)

cout.width(n)

设置输出宽度

setprecision(n)

cout.precision(n)

设置输出”精度”

setfill(x)

cout.fill(x)

设置填充字符

下述代码简单演示了操作算子的使用方法。

#include <iostream>#include <iomanip>#include <cmath>using namespace std;int main(){    cout << fixed;    cout << setw(6) << left << "N"         << setw(12) << left << "log2N"         << setw(12) << right << "log10N" << endl;    cout << "-----------------------------------------" << endl;    for (auto n = 1; n<=10; n++){        cout << setw(6) << setfill('-') << left << n << setfill(' ');        cout << setw(12) << setprecision(4) << left << log2(n);        cout << setw(12) << setprecision(5) << right << log10(n) << endl;    }    return 0;}

上述代码的执行结果为:

N     log2N             log10N-----------------------------------------1-----0.0000           0.000002-----1.0000           0.301033-----1.5850           0.477124-----2.0000           0.602065-----2.3219           0.698976-----2.5850           0.778157-----2.8074           0.845108-----3.0000           0.903099-----3.1699           0.9542410----3.3219           1.00000

本案例节选自作者编写的教材及配套实验指导书。

《C++编程基础及应用》(高等教育出版社,出版过程中)

《Python编程基础及应用》,高等教育出版社

《Python编程基础及应用实验教程》,高等教育出版社

高校教师同行如果期望索取样书,教学支持资料,加群,请私信作者,联系时请提供学校及个人姓名为盼,各高校在读学生勿扰为谢。

青少年读者们如果期望系统性地学习Python及C/C++程序设计语言,欢迎尝试下述今日头条(西瓜)免费视频课程。

C/C++从入门到放弃(重庆大学现场版)

Python编程基础及应用(重庆大学现场版)

标签: #c语言中的input怎么用 #c语言double的输出格式 #c语言中字符串的输出格式 #c语言库函数pow怎么用 #c语言 移位