哪些 iomanip 操纵器是“粘性的”?

发布于 2024-08-07 04:35:28 字数 729 浏览 9 评论 0原文

我最近在创建 stringstream 时遇到了问题,因为我错误地认为 std::setw() 会影响每次插入的 stringstream,直到我显式更改它。然而,插入后它总是被取消设置。

// With timestruct with value of 'Oct 7 9:04 AM'
std::stringstream ss;
ss.fill('0'); ss.setf(ios::right, ios::adjustfield);
ss << setw(2) << timestruct.tm_mday;
ss << timestruct.tm_hour;
ss << timestruct.tm_min;
std::string filingTime = ss.str(); // BAD: '0794'

所以,我有很多问题:

  • 为什么 setw() 是这样的?
  • 其他操纵者也是这样吗?
  • std::ios_base::width()std::setw() 之间的行为是否存在差异?
  • 最后是否有在线参考资料清楚地记录了这种行为?我的供应商文档(MS Visual Studio 2005)似乎没有清楚地表明这一点。

I recently had a problem creating a stringstream due to the fact that I incorrectly assumed std::setw() would affect the stringstream for every insertion, until I changed it explicitly. However, it is always unset after the insertion.

// With timestruct with value of 'Oct 7 9:04 AM'
std::stringstream ss;
ss.fill('0'); ss.setf(ios::right, ios::adjustfield);
ss << setw(2) << timestruct.tm_mday;
ss << timestruct.tm_hour;
ss << timestruct.tm_min;
std::string filingTime = ss.str(); // BAD: '0794'

So, I have a number of questions:

  • Why is setw() this way?
  • Are any other manipulators this way?
  • Is there a difference in behavior between std::ios_base::width() and std::setw()?
  • Finally is there an online reference that clearly documents this behavior? My vendor documentation (MS Visual Studio 2005) doesn't seem to clearly show this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

初见 2024-08-14 04:35:28

以下评论中的重要说明:

马丁:

@Chareles:那么根据这个要求,所有操纵器都是粘性的。除了 setw 之外,它似乎在使用后会被重置。

查尔斯:

正是如此! setw 表现出不同的唯一原因是因为格式化输出操作需要显式 .width(0) 输出流。

以下是得出上述结论的讨论:


查看代码,以下操纵器返回一个对象而不是流:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

这是一种仅将操作应用于应用于流的下一个对象的常见技术。不幸的是,这并不妨碍它们具有粘性。测试表明,除了 setw 之外,所有这些都是粘性的。

setiosflags:  Sticky
resetiosflags:Sticky
setbase:      Sticky
setfill:      Sticky
setprecision: Sticky

所有其他操纵器返回一个流对象。因此,它们更改的任何状态信息都必须记录在流对象中,因此是永久性的(直到另一个操纵器更改状态)。因此以下操纵器必须是粘性操纵器。

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

这些操纵器实际上对流本身而不是流对象执行操作(尽管从技术上讲,流是流对象状态的一部分)。但我不相信它们会影响流对象状态的任何其他部分。

ws/ endl/ ends/ flush

结论是 setw 似乎是我的版本中唯一不粘的操纵器。

对于 Charles 来说,有一个简单的技巧,可以只影响链中的下一个项目:
以下是如何使用对象临时更改状态然后通过使用对象将其恢复的示例:

#include <iostream>
#include <iomanip>

// Private object constructed by the format object PutSquareBracket
struct SquareBracktAroundNextItem
{
    SquareBracktAroundNextItem(std::ostream& str)
        :m_str(str)
    {}
    std::ostream& m_str;
};

// New Format Object
struct PutSquareBracket
{};

// Format object passed to stream.
// All it does is return an object that can maintain state away from the
// stream object (so that it is not STICKY)
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data)
{
    return SquareBracktAroundNextItem(str);
}

// The Non Sticky formatting.
// Here we temporariy set formating to fixed with a precision of 10.
// After the next value is printed we return the stream to the original state
// Then return the stream for normal processing.
template<typename T>
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data)
{
    std::ios_base::fmtflags flags               = bracket.m_str.flags();
    std::streamsize         currentPrecision    = bracket.m_str.precision();

    bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']';

    bracket.m_str.flags(flags);

    return bracket.m_str;
}


int main()
{

    std::cout << 5.34 << "\n"                        // Before 
              << PutSquareBracket() << 5.34 << "\n"  // Temp change settings.
              << 5.34 << "\n";                       // After
}


> ./a.out 
5.34
[5.3400000000]
5.34

Important notes from the comments below:

By Martin:

@Chareles: Then by this requirement all manipulators are sticky. Except setw which seems to be reset after use.

By Charles:

Exactly! and the only reason that setw appears to behave differently is because there are requirements on formatted output operations to explicitly .width(0) the output stream.

The following is the discussion that lead to the above conclusion:


Looking at the code the following manipulators return an object rather than a stream:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

This is a common technique to apply an operation to only the next object that is applied to the stream. Unfortunately this does not preclude them from being sticky. Tests indicate that all of them except setw are sticky.

setiosflags:  Sticky
resetiosflags:Sticky
setbase:      Sticky
setfill:      Sticky
setprecision: Sticky

All the other manipulators return a stream object. Thus any state information they change must be recorded in the stream object and is thus permanent (until another manipulator changes the state). Thus the following manipulators must be Sticky manipulators.

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

These manipulators actually perform an operation on the stream itself rather than the stream object (Though technically the stream is part of the stream objects state). But I do not believe they affect any other part of the stream objects state.

ws/ endl/ ends/ flush

The conclusion is that setw seems to be the only manipulator on my version that is not sticky.

For Charles a simple trick to affect only the next item in the chain:
Here is an Example how an object can be used to temporaily change the state then put it back by the use of an object:

#include <iostream>
#include <iomanip>

// Private object constructed by the format object PutSquareBracket
struct SquareBracktAroundNextItem
{
    SquareBracktAroundNextItem(std::ostream& str)
        :m_str(str)
    {}
    std::ostream& m_str;
};

// New Format Object
struct PutSquareBracket
{};

// Format object passed to stream.
// All it does is return an object that can maintain state away from the
// stream object (so that it is not STICKY)
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data)
{
    return SquareBracktAroundNextItem(str);
}

// The Non Sticky formatting.
// Here we temporariy set formating to fixed with a precision of 10.
// After the next value is printed we return the stream to the original state
// Then return the stream for normal processing.
template<typename T>
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data)
{
    std::ios_base::fmtflags flags               = bracket.m_str.flags();
    std::streamsize         currentPrecision    = bracket.m_str.precision();

    bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']';

    bracket.m_str.flags(flags);

    return bracket.m_str;
}


int main()
{

    std::cout << 5.34 << "\n"                        // Before 
              << PutSquareBracket() << 5.34 << "\n"  // Temp change settings.
              << 5.34 << "\n";                       // After
}


> ./a.out 
5.34
[5.3400000000]
5.34
蓝颜夕 2024-08-14 04:35:28

width 看起来不“粘性”的原因是某些操作保证在输出流上调用 .width(0)。它们是:

21.3.7.9 [lib.string.io]:

template<class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os,
               const basic_string<charT,traits,Allocator>& str);

22.2.2.2.2 [lib.facet.num.put.virtuals]:num_putdo_put 重载> 模板。这些由采用 basic_ostream 和内置数字类型的 operator<< 重载使用。

22.2.6.2.2 [lib.locale.money.put.virtuals]:money_put 模板的所有 do_put 重载。

27.6.2.5.4 [lib.ostream.inserters.character]:重载 operator<<,采用 basic_ostream 和 basic_ostream 实例化的 char 类型之一或 < code>char、signed charunsigned char 或指向这些 char 类型数组的指针。

老实说,我不确定这样做的理由,但 ostream 的其他状态不应通过格式化输出函数重置。当然,如果输出操作失败,则可能会设置诸如 badbitfailbit 之类的内容,但这应该是预料之中的。

我能想到的重置宽度的唯一原因是,当尝试输出某些分隔字段时,如果分隔符被填充,这可能会令人惊讶。

例如,

std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';

"   4.5     |   3.6      \n"

要“纠正”这将需要:

std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';

而使用重置宽度,可以使用较短的值生成所需的输出:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';

The reason that width does not appear to be 'sticky' is that certain operations are guaranteed to call .width(0) on an output stream. Those are:

21.3.7.9 [lib.string.io]:

template<class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os,
               const basic_string<charT,traits,Allocator>& str);

22.2.2.2.2 [lib.facet.num.put.virtuals]: All do_put overloads for the num_put template. These are used by overloads of operator<< taking a basic_ostream and a built in numeric type.

22.2.6.2.2 [lib.locale.money.put.virtuals]: All do_put overloads for the money_put template.

27.6.2.5.4 [lib.ostream.inserters.character]: Overloads of operator<< taking a basic_ostream and one of the char type of the basic_ostream instantiation or char, signed char or unsigned char or pointers to arrays of these char types.

To be honest I'm not sure of the rationale for this, but no other states of an ostream should be reset by formatted output functions. Of course, things like badbit and failbit may be set if there is a failure in the output operation, but that should be expected.

The only reason that I can think of for resetting the width is that it might be surprising if, when trying to output some delimited fields, your delimiters were padded.

E.g.

std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';

"   4.5     |   3.6      \n"

To 'correct' this would take:

std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';

whereas with a resetting width, the desired output can be generated with the shorter:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';
∞觅青森が 2024-08-14 04:35:28

setw() 仅影响下一次插入。这就是 setw() 的行为方式。 setw() 的行为与 ios_base::width() 相同。我从 cplusplus.com 获取了 setw() 信息。

您可以在此处找到操纵器的完整列表。从该链接开始,所有流标志都应保持设置状态,直到被另一个操纵器更改为止。关于 leftrightinternal 操纵器的一点注意事项:它们与其他标志一样,并且do会一直持续到更改为止。然而,它们只有在设置了流的宽度时才有效,并且必须每行都设置宽度。因此,例如

cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;

会给您

>     a
>     b
>     c

cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;

会给您

>     a
>b
>c

输入和输出操纵器不具有粘性,并且仅在使用它们时出现一次。参数化操纵器各不相同,以下是每个操纵器的简要说明:

setiosflags允许您手动设置标志,可以在 找到标志列表这里,所以它是粘性的。

resetiosflags 的行为类似于 setiosflags 除非它取消设置指定的标志。

setbase 设置插入的整数的基数流(因此 17 在基数 16 中将是“11”,在基数 2 中将是“10001”)。

setfill 设置要插入的填充字符使用 setw 时的流。

set precision 设置要使用的小数精度插入浮点值时。

setw 仅使下一个插入指定通过填充 setfill

setw() only affects the next insertion. That's just the way setw() behaves. The behavior of setw() is the same as ios_base::width(). I got my setw() information from cplusplus.com.

You can find a full list of manipulators here. From that link, all the stream flags should stay set until changed by another manipulator. One note about the left, right and internal manipulators: They are like the other flags and do persist until changed. However, they only have an effect when the width of the stream is set, and the width must be set every line. So, for example

cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;

would give you

>     a
>     b
>     c

but

cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;

would give you

>     a
>b
>c

The Input and Output manipulators are not sticky and only occur once where they are used. The parameterized manipulators are each different, here's a brief description of each:

setiosflags lets you manually set flags, a list of which can be found here, so it is sticky.

resetiosflags behaves similar to setiosflags except it unsets the specified flags.

setbase sets the base of integers inserted into the stream (so 17 in base 16 would be "11", and in base 2 would be "10001").

setfill sets the fill character to insert in the stream when setw is used.

setprecision sets the decimal precision to be used when inserting floating point values.

setw makes only the next insertion the specified width by filling with the character specified in setfill

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文