简体   繁体   English

使用fill和setw探索C ++,探索格式化挑战,对输出感到困惑

[英]Exploring C++, Exploring Formatting Challenge, using fill and setw, confused about the output

So the challenge is to write a program with this output: 因此,挑战在于编写具有以下输出的程序:

000042
420000
42
-42-

My first attempt was something like this: 我的第一次尝试是这样的:

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;

Which gave me something like this: 这给了我这样的东西:

000042
42
000042
42-- (sometimes just -42)

Here is the author's solution: 这是作者的解决方案:

cout << setfill('0') << setw(6) << 42 << endl;
cout << left << setw(6) << 42 << endl;
cout << 42 << endl;
cout << setfill('-') << setw(4) << -42 << endl;

Why does the author only use setfill once? 为什么作者只使用一次setfill? How does setfill work for the first two lines but stop all of a sudden at line 3? 前两行的setfill如何工作,但在第3行突然停止? How does putting setfill('-') and setw(4) before -42 produce -42- instead of --42? 如何将setfill('-')和setw(4)放在-42之前会产生-42-而不是--42? What is the left alignment operator needed for? 左对齐运算符需要什么?

Finally why doesn't my version produce the correct output? 最后,为什么我的版本无法产生正确的输出?

setw() only affects the next output. setw()仅影响下一个输出。 Every other question is pretty much a consequence of this behaviour. 其他所有问题几乎都是这种行为的结果。 That is, unless you try to widen the output, no fill character or alignment will take place. 也就是说,除非您尝试加宽输出,否则不会发生填充字符或对齐。

std::setw determines the width of the next output field only, but std::setfill sets the fill character used for all further print operations of the stream object it is passed to. std::setw确定下一个输出字段的宽度,但是std::setfill设置填充字符,该填充字符用于将其传递到的流对象的所有其他打印操作。

So the fill character does not actually suddenly stop working on the third line of output. 因此,填充字符实际上并不会突然停止在输出的第三行上工作。 But because there is no std::setw manipulator before that output is generated, the fill character is simple not used. 但是因为在生成该输出之前没有std::setw操纵器,所以填充字符很简单,不使用。

std::left , like std::setfill , takes effect until cancelled. std::left ,像std::setfill ,生效,直至取消。 So, after being set for the second line of output, it still takes effect when the fourth line is generated. 因此,在为第二行输出设置后,它在生成第四行时仍然有效。 Since in the fourth line, the fill character is - , this character is appended to the end of the line. 由于在第四行中,填充字符为- ,因此此字符将附加到该行的末尾。 (The - at the beginning is due to its being explicitly specified as part of the output string.) (开头的-是由于将其明确指定为输出字符串的一部分。)


Quotes from the Standard (C++11) for reference. 引用标准(C ++ 11)引用。

  1. std::setfill is described in 27.7.4/5: std::setfill在27.7.4 / 5中进行了描述:

    unspecified setfill(char_type c);

    Returns: An object of unspecified type such that if out is an object of type basic_ostream and c has type charT then the expression out << setfill(c) behaves as if it called f(out, c) , where the function f is defined as: 返回:未指定类型的对象,例如,如果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); } 

    The expression out << setfill(c) shall have type basic_ostream<charT, traits>& and value out . out << setfill(c)表达式的类型必须为basic_ostream<charT, traits>&且值为out

    So calls the fill() function of the stream, which in turn is described in 27.5.5.3/14: 因此调用流的fill()函数,该函数在27.5.5.3/14中进行了描述:

    char_type fill(char_type fillch);
    Postcondition: traits::eq(fillch, fill()) 后置条件: traits::eq(fillch, fill())

    In other words, it modifies a property that is part of the traits of the stream. 换句话说,它修改了作为流特征的一部分的属性。 Suffice it to say that traits are valid until explicitly modified (and not only for the next output field). 可以说,特征在被显式修改之前是有效的(不仅限于下一个输出字段)。

  2. The situation is similar for std::left , which sets the adjustfield parameter of the stream, as described in 27.5.6.2/3: std::left的情况与此类似,它设置了流的adjustfield参数,如27.5.6.2/3中所述:

    ios_base& left(ios_base& str);
    Effects: Calls str.setf(ios_base::left, ios_base::adjustfield). 效果:调用str.setf(ios_base::left, ios_base::adjustfield).

  3. std::setw , on the other hand, is described in 27.7.5/7 另一方面, std::setw在27.7.5 / 7中描述

    unspecified setw(int n); 未指定setw(int n);
    Returns: An object of unspecified type such that if out is an instance of basic_ostream<charT, traits> then the expression out << setw(n) behaves as if it called f(out, n) , or if in is an object of type basic_istream<charT, traits> then the expression in >> setw(n) behaves as if it called f(in, n) , where the function f is defined as: 返回:未指定类型的对象,例如,如果out是basic_ostream<charT, traits>的实例,则表达式out << setw(n)行为就像调用f(out, n) ,或者in是对象的对象类型basic_istream<charT, traits>in >> setw(n)的表达式的行为就像调用了f(in, n) ,其中将函数f定义为:

     void f(ios_base& str, int n) { // set width str.width(n); } 

    The expression out << setw(n) shall have type basic_ostream& and value out. out << setw(n)表达式应具有basic_ostream&类型,其值应为out。 The expression in >> setw(n) shall have type basic_istream& and value in. >> setw(n)中的表达式的类型应为basic_istream&,且值为in。

    And indeed, the property width is reset to 0 after the occurrence of a formatted output operation. 实际上,在发生格式化输出操作之后,属性width会重置为0 Eg in the description of operator<< when applied to strings (ie when you use the << -syntax to output a string), the order of operations in described by 27.7.3.6.4/5 (emphasis mine): 例如,在将operator<<应用于字符串的描述中(即,当您使用<< -syntax输出字符串时),操作顺序由27.7.3.6.4 / 5(强调我的)描述:

    Padding is determined as described in 22.4.2.2.2. 如22.4.2.2.2中所述确定填充。 The n characters starting at s are widened using out.widen (27.5.5.3). 以s开头的n个字符使用out.widen (27.5.5.3)进行扩展。 The widened characters and any required padding are inserted into out. 加宽的字符和任何所需的填充插入到外面。 Calls width(0) . 调用width(0)

    In the case of a formatted output operation for a numeric types (27.7.3.6.2/1,2,3), the std::num_put (which is a std::locale::facet ) template is used to convert the number into a string and print it. 如果是数字类型(27.7.3.6.2 / 1,2,3)的格式化输出操作,则使用std::num_put (这是std::locale::facet )模板来转换数字成字符串并打印出来。 22.4.2.2 describes in detail how that operation is performed, and its final step (after the string has been outputted) resets the width to 0 (22.4.2.2.2/5, stage 3, at the end): 22.4.2.2详细描述了该操作的执行方式,其最后一步(在输出字符串之后)将宽度重置为0 (最后是阶段3.22.4.2.2.2 / 5):

    str.width(0) is called str.width(0)被称为

Further study of the effects of the various IO manipulators reveals that in fact, std:setw is the only manipulator that takes effect only on the following (formatted) output operation. 对各种IO操纵器的效果的进一步研究表明,实际上, std:setw唯一仅对以下(格式化的)输出操作起作用的操纵器。 See also Which iomanipulators are 'sticky'? 另请参见哪些iomanipulator是“粘性”的? , especially Charles Bailey's answer, for a comparison of the "stickiness" of the different manipulators. ,特别是Charles Bailey的答案,用于比较不同机械手的“粘性”。

setfill (like most ostream modifiers, eg hex , dec , left , the binary one, precision IIRC too) keep their value once you change them. setfill (就像大多数ostream修饰符,例如hexdecleft ,二进制1,精度IIRC一样)一旦更改就保持其值。

Let me back up a bit. 让我备份一下。

std::ostream (the class cout is an instance of) has internal formatting values. std::ostream (类cout是其实例)具有内部格式值。 Internal variables that tell it what the filler char is, whether to print in hex etc. 内部变量,告诉它填充字符是什么,是否以十六进制打印等。

Most of these values (maybe all except for width ) don't change unless YOU change them. 除非您更改它们,否则大多数这些值(也许是width除外)都不会改变。 So once you set the fill , it stays that way until YOU change it to something else. 因此,一旦您设置fill ,它就会一直保持这种状态,直到您将其更改为其他内容为止。

width is different (unfortunately :( I'm sure they had their reasons). After you set it, it only stays for the next item you write - then it resets to 0. width是不同的(不幸的是:(我确定他们是有原因的。)设置好之后,它仅保留在您写的下一个项目上,然后重置为0。

Second thing you seem to have missed, is that the width and fill etc. don't print anything, they only change the internal state and hence apply to what comes after them . 您似乎错过的第二件事是widthfill等不打印任何内容,它们仅更改内部状态,因此适用于其后的内容

So lets analyze your solution: 因此,让我们分析您的解决方案:

You set the fill to 0, set the width to 6, print 42 - which is 2 characters and hence expanded to 6 by adding four 0-s (to the left, because the default is right justified text). 您将fill设置为0,将width设置为6,打印42-这是2个字符,因此通过添加四个0-s扩展到6(在左边,因为默认为右对齐文本)。 Now you're printed something, so width resets (!) 现在您已经打印了一些东西,因此width重置(!)

Now you started a new line ( endl ), printed 42 (bit the width is reset, so it just prints it at whatever width, no need to fill!), set the fill to 0 (it already was! fill stays until you change it), set the width to 6 (but for the next value, not the one already printed!) and go for a new line. 现在,您开始一个新行( endl ),打印42(重置宽度,所以它只打印任何宽度,无需填充!),将fill设置为0(它已经是! fill保持不变,直到您更改) ),将宽度设置为6(但是要输入下一个值,而不是已经打印的宽度!),然后换一个新行。

Now you print 42 ( width is 6, so it fills the 4 extra chars needed with 0) (the width reset after your print) and go for a new line. 现在,您打印42( width为6,因此它将用0填充所需的4个额外字符)(打印后重新设置width )并换行。

You set the fill to - and the width to 4, and print 42 - which is expanded to 4 characters by adding two - on the left side (not like you wrote... weird that), and set the fill again to - (unneeded, and also unused as you don't print anything after that - setfill and other formattings only affects things that happen after them!) 您将填充设置为-,宽度设置为4,然后打印42-通过在左侧添加两个字符将其扩展为4个字符(这与您写的...很奇怪)不同,然后再次将填充设置为-(不需要,也没有使用,因为在此之后您什么都不打印setfill和其他格式只会影响在它们之后发生的事情!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM