我最近遇到了一个问题stringstream,因为我错误地认为std::setw()这会影响每次插入的字符串流,直到我明确地更改它.但是,插入后总是未设置.
// 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'
Run Code Online (Sandbox Code Playgroud)
所以,我有很多问题:
setw()这样?std::ios_base::width()和std::setw()?Mar*_*ork 84
以下评论的重要说明:
马丁:
@Chareles:然后通过这个要求,所有操纵器都是粘性的.除了在使用后似乎重置的setw.
查尔斯:
究竟!并且setw表现出不同行为的唯一原因是因为格式化输出操作需要显式地.width(0)输出流.
以下是导致上述结论的讨论:
查看代码,以下操纵器返回一个对象而不是一个流:
setiosflags
resetiosflags
setbase
setfill
setprecision
setw
Run Code Online (Sandbox Code Playgroud)
这是将操作仅应用于应用于流的下一个对象的常用技术.不幸的是,这并不妨碍它们变得粘稠.测试表明所有这些setw都是粘性的.
setiosflags: Sticky
resetiosflags:Sticky
setbase: Sticky
setfill: Sticky
setprecision: Sticky
Run Code Online (Sandbox Code Playgroud)
所有其他操纵器都返回一个流对象.因此,它们改变的任何状态信息必须记录在流对象中,因此是永久的(直到另一个操纵器改变状态).因此,以下操纵器必须是粘性操纵器.
[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase
dec/ hex/ oct
fixed/ scientific
internal/ left/ right
Run Code Online (Sandbox Code Playgroud)
这些操纵器实际上对流本身而不是流对象执行操作(尽管从技术上讲,流是流对象状态的一部分).但我不相信它们会影响流对象状态的任何其他部分.
ws/ endl/ ends/ flush
Run Code Online (Sandbox Code Playgroud)
结论是setw似乎是我的版本中唯一没有粘性的操纵器.
对于查尔斯来说,一个简单的技巧只能影响链中的下一个项目:
下面是一个示例如何使用对象临时更改状态,然后通过使用对象将其放回:
#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
Run Code Online (Sandbox Code Playgroud)
CB *_*ley 30
其原因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);
Run Code Online (Sandbox Code Playgroud)
22.2.2.2.2 [lib.facet.num.put.virtuals]:模板的所有do_put重载num_put.这些用于operator<<采用a basic_ostream和内置数字类型的重载.
22.2.6.2.2 [lib.locale.money.put.virtuals]:模板的所有do_put重载money_put.
27.6.2.5.4 [lib.ostream.inserters.character]:的重载operator<<服用basic_ostream和焦炭类型basic_ostream实例化的一个或char签署char或unsigned char或指针这些字符类型的数组.
说实话,我不确定这个的基本原理,但是没有其他状态ostream应该通过格式化的输出函数重置.当然,如果输出操作出现故障,可以设置badbit和failbit设置,但这应该是预期的.
我可以想到重置宽度的唯一原因是,如果在尝试输出一些分隔字段时,您的分隔符被填充,这可能会令人惊讶.
例如
std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';
" 4.5 | 3.6 \n"
Run Code Online (Sandbox Code Playgroud)
要"纠正",这需要:
std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';
Run Code Online (Sandbox Code Playgroud)
而使用重置宽度,可以生成所需的输出更短的:
std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';
Run Code Online (Sandbox Code Playgroud)
setw()仅影响下一次插入.这就是setw()表现的方式.行为setw()是一样的ios_base::width().我setw()从cplusplus.com获取了我的信息.
您可以在此处找到完整的操纵器列表.从该链接开始,所有流标志应该设置为set,直到被另一个操纵器更改为止.一个说明有关left,right并internal操纵:他们就像其他标志和你继续下去,直到改变.但是,它们仅在设置流的宽度时有效,并且必须在每一行设置宽度.所以,例如
cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;
Run Code Online (Sandbox Code Playgroud)
会给你的
> a
> b
> c
Run Code Online (Sandbox Code Playgroud)
但
cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;
Run Code Online (Sandbox Code Playgroud)
会给你的
> a
>b
>c
Run Code Online (Sandbox Code Playgroud)
输入和输出操纵器不粘,只在使用它们时出现一次.参数化操纵器各不相同,这里是每个操作器的简要说明:
setiosflags让你手动设置标志,这里可以列出一个列表,所以它是粘性的.
resetiosflags行为类似于setiosflags除了它取消设置指定的标志.
setbase 设置插入流中的整数的基数(因此基数16中的17将是"11",而基数2中的17将是"10001").
setfill设置填充字符以在使用时插入流中setw.
setprecision 设置插入浮点值时要使用的小数精度.
setw 通过填充指定的字符,仅使下一个插入指定的宽度 setfill