如何理解 C++ std::setw 的不一致行为?

How to understand C++ std::setw 's inconsistent behaviour?

本文关键字:不一致 setw 何理解 C++ std      更新时间:2023-10-16

给定以下代码:

/*Formatting Output 
**Goal: practice using cout to format output to console
**Print the variables in three columns:
**Ints, Floats, Doubles
*/
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int a = 45;
float b = 45.323;
double c = 45.5468;
int aa = a + 9;
float bb = b + 9;
double cc = c + 9;
int aaa = aa + 9;
float bbb = bb + 9;
double ccc = cc + 9;
// 1st attempt :>
cout << "nnn" << "// 1st attempt :>" << "n";
cout << "12345678901234567890123456789012345678901234567890" << "n";
cout << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "n";
cout << a << setw(15) << b << setw(15) << c << "n";
cout << aa << setw(15) << bb << setw(15) << cc << "n";
cout << aaa << setw(15) << bbb << setw(15) << ccc << "n";

// 2nd attempt :>
cout << "nnn" << "// 2nd attempt :>" << "n";
cout << "12345678901234567890123456789012345678901234567890" << "n";
cout << std::left << std::setfill(' ') << std::setw(15)  << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "n";
cout << a << setw(15) << b << setw(15) << c << "n";
cout << std::left << std::setfill(' ') << setw(15) << aa << setw(15) << bb << setw(15) << cc << "n";
cout << aaa << setw(15) << bbb << setw(15) << ccc << "n";
// 3rd attempt :>
cout << "nnn" << "// 3rd attempt :>" << "n";
cout << "12345678901234567890123456789012345678901234567890" << "n";
cout << std::left << std::setfill(' ') << std::setw(15) << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "n";
cout << std::left << std::setfill(' ') << std::setw(15) << a << setw(15) << b << setw(15) << c << "n";
cout << std::left << std::setfill(' ') << std::setw(15) << aa << setw(15) << bb << setw(15) << cc << "n";
cout << std::left << std::setfill(' ') << std::setw(15) << aaa << setw(15) << bbb << setw(15) << ccc << "n";
cout << "12345678901234567890123456789012345678901234567890" << "n";
cout << std::right << std::setfill(' ') << std::setw(15) << "Ints" << setw(15) << "Floats" << setw(15) << "Doubles" << "n";
cout << std::right << std::setfill(' ') << std::setw(15) << a << setw(15) << b << setw(15) << c << "n";
cout << std::right << std::setfill(' ') << std::setw(15) << aa << setw(15) << bb << setw(15) << cc << "n";
cout << std::right << std::setfill(' ') << std::setw(15) << aaa << setw(15) << bbb << setw(15) << ccc << "n";
return 0;
}
// https://repl.it/@Tredekka/Cpp-Understanding-stdsetw

。我得到以下输出:

gcc version 4.6.3
// 1st attempt :>
12345678901234567890123456789012345678901234567890
Ints         Floats        Doubles
45         45.323        45.5468
54         54.323        54.5468
63         63.323        63.5468

// 2nd attempt :>
12345678901234567890123456789012345678901234567890
Ints           Floats         Doubles        
4545.323         45.5468        
54             54.323         54.5468        
6363.323         63.5468        

// 3rd attempt :>
12345678901234567890123456789012345678901234567890
Ints           Floats         Doubles        
45             45.323         45.5468        
54             54.323         54.5468        
63             63.323         63.5468        
12345678901234567890123456789012345678901234567890
Ints         Floats        Doubles
45         45.323        45.5468
54         54.323        54.5468
63         63.323        63.5468

注意:我故意与代码"不一致","因为"我试图理解<iomanip>&std::setw()代码的行为。

如果您查看第一次尝试的输出,您会注意到标题行"字符串"输出与数据行"数字"输出偏移... 虽然它"大部分"实现了在列中对齐内容的目的,但它既不准确也不一致。

在第二次尝试中,您将看到我发现如果我在行输出前面加上以下内容:

<< std::left << std::setfill(' ') << setw(15)

。然后我让行看起来正确(如标题和第二个数据行所示)......但是现在你会注意到第1和第3数据行是非常错误的:

4545.323         45.5468        
...       
6363.323         63.5468      

。如何"使用/执行"...

<< std::left << std::setfill(' ') << setw(15)

。影响setw()的"未来"执行

为了完整起见,我在第三次尝试中表明,可以使用<iomanip>&&std::setw()正确准确地对齐数据列(左或右)。但为什么会出现不一致呢?

(@WhozCraig的回答帮助我到达了我的位置,但没有深入研究到足以帮助我理解:(a)为什么它是"伪"有效的||(b) 为什么在你让它在"第一次"正常工作后,它会破坏"伪"功能。

这是您的问题:std::setw()不充当缓冲区,它会修改下一个表达式的计算方式。

您需要了解每个表达式的"范围"。具体来说,std::setfill(int)std::left/std::right更改默认行为,并且似乎一直持续到它们被另一个setfill()std::left/std::right覆盖为止。 另一方面,std::setw(int)似乎只影响它之后传递的任何内容(这很奇怪,因为我觉得我以前也见过它的行为像std::setfill和其他人)

所以,总结一下,你想要的更类似于这样:

int a = 45;
float b = 45.323;
double c = 45.5468;
int aa = a + 9;
float bb = b + 9;
double cc = c + 9;
int aaa = aa + 9;
float bbb = bb + 9;
double ccc = cc + 9;

std::cout << std::endl << std::endl << std::endl;
std::cout << std::left << std::setfill('~');
// 1st attempt :>
std::cout << "// 1st attempt :>" << std::endl;
std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;

std::cout << std::endl << std::endl << std::endl << std::setfill('*');

// 2nd attempt :>
std::cout << "// 2nd attempt :>" << std::endl;
std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;

std::cout << std::endl << std::endl << std::endl;

// 3rd attempt :>
std::cout << "// 3rd attempt :>" << std::endl;
std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;
std::cout << "12345678901234567890123456789012345678901234567890" << std::endl;
std::cout << std::right << std::setfill(' ');
std::cout << std::setw(10) << "Ints" << std::setw(10) << "Floats" << std::setw(10) << "Doubles" << std::endl;
std::cout << std::setw(10) << a   << std::setw(10) << b   << std::setw(10) << c   << std::endl;
std::cout << std::setw(10) << aa  << std::setw(10) << bb  << std::setw(10) << cc  << std::endl;
std::cout << std::setw(10) << aaa << std::setw(10) << bbb << std::setw(10) << ccc << std::endl;
std::cout << std::endl << std::endl << std::endl;

(您会注意到我还"n"更改为std::endl,这会在每行之后额外刷新缓冲区。