所以挑战是用这个输出编写一个程序:
000042
420000
42
-42-
我的第一次尝试是这样的:
int fortyTwo = 42;
cout << setfill('0') << setw(6) << fortyTwo << endl;
cout << fortyTwo << setfill('0') << setw(6) << endl;
cout << fortyTwo << endl;
cout << setfill('-') << setw(4) << fortyTwo << setfill('-') << endl;
这给了我这样的东西:
000042
42
000042
42-- (sometimes just -42)
以下是作者的解决方案:
cout << setfill('0') << setw(6) << 42 << endl;
cout << left << setw(6) << 42 << endl;
cout << 42 << endl;
cout << setfill('-') << setw(4) << -42 << endl;
为什么作者只使用一次setfill? setfill如何为前两行工作,但在第3行突然停止? 如何在-42之前设置setfill(' - ')和setw(4)而不是-42? 什么是左对齐运算符?
最后为什么我的版本没有产生正确的输出?
答案 0 :(得分:0)
setw()仅影响下一个输出。其他每个问题都是这种行为的结果。也就是说,除非您尝试加宽输出,否则不会出现填充字符或对齐。
答案 1 :(得分:0)
std::setw
仅确定 next 输出字段的宽度,但std::setfill
设置用于所有的填充字符的进一步打印操作传递给它的流对象。
所以填充字符实际上并没有突然停止在第三行输出上工作。但是因为在生成输出之前没有std::setw
操纵器,所以填充字符很简单,不使用。
std::left
与std::setfill
一样,在取消之前生效。因此,在设置第二行输出后,它仍然会在生成第四行时生效。由于在第四行中,填充字符为-
,因此该字符将附加到该行的末尾。 (开头的-
是由于它被明确指定为输出字符串的一部分。)
标准引用(C ++ 11)以供参考。
std::setfill
在27.7.4 / 5中描述:
unspecified setfill(char_type c);
返回:未指定类型的对象,如果out是basic_ostream类型的对象而c的类型为
charT
,则表达式out << setfill(c)
的行为就像它调用f(out, c)
一样,其中函数f
定义为:template<class charT, class traits> void f(basic_ios<charT,traits>& str, charT c) { // set fill character str.fill(c); }
表达式
out << setfill(c)
的类型为basic_ostream<charT, traits>&
,值为out
。
所以调用流的fill()
函数,而这个函数又在27.5.5.3/14中描述:
char_type fill(char_type fillch);
后置条件:traits::eq(fillch, fill())
换句话说,它修改了属于流的 traits 的属性。只需说明特征在显式修改之前有效(并且不仅适用于下一个输出字段)。
std::left
的情况类似,它设置了流的 adjustfield 参数,如27.5.6.2/3中所述:
ios_base& left(ios_base& str);
效果:致电str.setf(ios_base::left, ios_base::adjustfield).
std::setw
在27.7.5 / 7
未指定
setw(int n);
返回:未指定类型的对象,如果out是basic_ostream<charT, traits>
的实例,则表达式out << setw(n)
的行为就像调用f(out, n)
一样,或者如果in是{{1}类型的对象然后,表达式basic_istream<charT, traits>
的行为就像调用in >> setw(n)
一样,其中函数f(in, n)
定义为:f
表达式&lt;&lt; setw(n)的类型为basic_ostream&amp;和价值。 &gt;&gt;中的表达式setw(n)的类型为basic_istream&amp;和价值。
事实上,在格式化输出操作发生后,属性void f(ios_base& str, int n) {
// set width
str.width(n);
}
将重置为width
。例如。在应用于字符串时0
的描述中(即当您使用operator<<
- 语法输出字符串时),27.7.3.6.4 / 5中描述的操作顺序(强调我的) :
填充如22.4.2.2.2中所述确定。从s开始的n个字符使用
<<
(27.5.5.3)加宽。加宽的字符和任何所需的填充都插入到外面。 致电out.widen
。
对于数字类型(27.7.3.6.2 / 1,2,3)的格式化输出操作,width(0)
(这是std::num_put
)模板用于转换将数字转换成字符串并打印出来。 22.4.2.2详细描述了如何执行该操作,并且其最后一步(在输出字符串之后)将宽度重置为std::locale::facet
(22.4.2.2.2 / 5,阶段3,结尾):
0
被称为
对各种IO操纵器的影响的进一步研究表明,事实上,str.width(0)
是唯一仅对以下(格式化)输出操作生效的操纵器。另请参阅Which iomanipulators are 'sticky'?,特别是Charles Bailey的回答,以比较不同操纵者的“粘性”。
答案 2 :(得分:0)
setfill
(与大多数ostream
修饰符一样,例如hex
,dec
,left
,二进制修饰符,精度IIRC)一旦保留其值改变它们。
让我稍微补充一下。
std::ostream
(类cout
是其实例)具有内部格式值。内部变量告诉它填充字符是什么,是否以十六进制打印等。
除非您更改这些值,否则大多数这些值(可能除width
之外的所有值)都不会更改。因此,一旦设置了fill
,就会保持这种状态,直到您将其更改为其他内容。
width
是不同的(不幸的是:(我确定他们有他们的理由)。设置之后,它只停留在你写的下一个项目 - 然后重置为0。
你似乎错过的第二件事是,width
和fill
等不打印任何东西,它们只改变内部状态,因此适用于它们后面的内容强>
让我们分析一下你的解决方案:
您将fill
设置为0,将width
设置为6,打印42 - 这是2个字符,因此通过添加4个0-s扩展为6(左侧,因为默认值是正确的文本)。现在你打印了一些东西,所以width
重置(!)
现在你开始了一个新的行(endl
),打印了42(宽度被重置,所以它只是以任何宽度打印,不需要填充!),将fill
设置为0(它已经是!fill
一直保持到你改变它为止),将宽度设置为6(但是对于下一个值,而不是已经打印的值!)并换一个新行
现在你打印42(width
是6,所以它填充了0所需的4个额外字符)(打印后width
重置)并换一个新行。
您将填充设置为 - 并将宽度设置为4,并将打印42 - 通过添加两个扩展为4个字符 - 在左侧(不像您写的那样......很奇怪),并再次设置填充to - (不需要,也没用,因为你之后不打印任何东西 - setfill
和其他格式只会影响他们之后发生的事情!)