探索C ++,探索格式化挑战,使用fill和setw,混淆输出

时间:2013-07-19 01:32:57

标签: c++

所以挑战是用这个输出编写一个程序:

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? 什么是左对齐运算符?

最后为什么我的版本没有产生正确的输出?

3 个答案:

答案 0 :(得分:0)

setw()仅影响下一个输出。其他每个问题都是这种行为的结果。也就是说,除非您尝试加宽输出,否则不会出现填充字符或对齐。

答案 1 :(得分:0)

std::setw仅确定 next 输出字段的宽度,但std::setfill设置用于所有的填充字符的进一步打印操作传递给它的流对象。

所以填充字符实际上并没有突然停止在第三行输出上工作。但是因为在生成输出之前没有std::setw操纵器,所以填充字符很简单,不使用。

std::leftstd::setfill一样,在取消之前生效。因此,在设置第二行输出后,它仍然会在生成第四行时生效。由于在第四行中,填充字符为-,因此该字符将附加到该行的末尾。 (开头的-是由于它被明确指定为输出字符串的一部分。)


标准引用(C ++ 11)以供参考。

  1. 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 的属性。只需说明特征在显式修改之前有效(并且不仅适用于下一个输出字段)。

  2. std::left的情况类似,它设置了流的 adjustfield 参数,如27.5.6.2/3中所述:

      

    ios_base& left(ios_base& str);
      效果:致电str.setf(ios_base::left, ios_base::adjustfield).

  3. 另一方面,
  4. 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被称为

  5. 对各种IO操纵器的影响的进一步研究表明,事实上,str.width(0)唯一仅对以下(格式化)输出操作生效的操纵器。另请参阅Which iomanipulators are 'sticky'?,特别是Charles Bailey的回答,以比较不同操纵者的“粘性”。

答案 2 :(得分:0)

setfill(与大多数ostream修饰符一样,例如hexdecleft,二进制修饰符,精度IIRC)一旦保留其值改变它们。

让我稍微补充一下。

std::ostream(类cout是其实例)具有内部格式值。内部变量告诉它填充字符是什么,是否以十六进制打印等。

除非您更改这些值,否则大多数这些值(可能除width之外的所有值)都不会更改。因此,一旦设置了fill,就会保持这种状态,直到您将其更改为其他内容。

width是不同的(不幸的是:(我确定他们有他们的理由)。设置之后,它只停留在你写的下一个项目 - 然后重置为0。

你似乎错过的第二件事是,widthfill等不打印任何东西,它们只改变内部状态,因此适用于它们后面的内容

让我们分析一下你的解决方案:

您将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和其他格式只会影响他们之后发生的事情!)