假设我有这样的代码:
void printHex(std::ostream& x){
x<<std::hex<<123;
}
..
int main(){
std::cout<<100; // prints 100 base 10
printHex(std::cout); //prints 123 in hex
std::cout<<73; //problem! prints 73 in hex..
}
我的问题是,在从函数返回后,是否有任何方法可以将cout的状态“恢复”到原来的状态? (有点像std :: boolalpha和std :: noboolalpha ..)?
感谢。
答案 0 :(得分:76)
您需要#include <iostream>
或#include <ios>
,然后才需要:
std::ios_base::fmtflags f( cout.flags() );
//Your code here...
cout.flags( f );
您可以将这些内容放在功能的开头和结尾,或者查看this answer如何将其与RAII一起使用。
答案 1 :(得分:55)
Boost IO Stream State Saver似乎正是您所需要的。 : - )
基于您的代码段的示例:
void printHex(std::ostream& x) {
boost::io::ios_flags_saver ifs(x);
x << std::hex << 123;
}
答案 2 :(得分:35)
请注意,此处提供的答案无法恢复std::cout
的完整状态。例如,std::setfill
将&#34;坚持&#34;甚至在致电.flags()
之后。更好的解决方案是使用.copyfmt
:
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout
<< std::hex
<< std::setw(8)
<< std::setfill('0')
<< 0xDECEA5ED
<< std::endl;
std::cout.copyfmt(oldState);
std::cout
<< std::setw(15)
<< std::left
<< "case closed"
<< std::endl;
将打印:
case closed
而不是:
case closed0000
答案 3 :(得分:19)
我使用此答案中的示例代码创建了一个RAII类。如果你有一个在iostream上设置标志的函数的多个返回路径,那么这种技术的最大优势就来了。无论使用哪个返回路径,都将始终调用析构函数,并始终重置标志。当函数返回时,没有机会忘记恢复标志。
class IosFlagSaver {
public:
explicit IosFlagSaver(std::ostream& _ios):
ios(_ios),
f(_ios.flags()) {
}
~IosFlagSaver() {
ios.flags(f);
}
IosFlagSaver(const IosFlagSaver &rhs) = delete;
IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;
private:
std::ostream& ios;
std::ios::fmtflags f;
};
然后,只要您想保存当前标志状态,就可以通过创建IosFlagSaver的本地实例来使用它。当此实例超出范围时,将恢复标志状态。
void f(int i) {
IosFlagSaver iosfs(std::cout);
std::cout << i << " " << std::hex << i << " ";
if (i < 100) {
std::cout << std::endl;
return;
}
std::cout << std::oct << i << std::endl;
}
答案 4 :(得分:7)
通过一些修改使输出更具可读性:
void printHex(std::ostream& x) {
ios::fmtflags f(x.flags());
x << std::hex << 123 << "\n";
x.flags(f);
}
int main() {
std::cout << 100 << "\n"; // prints 100 base 10
printHex(std::cout); // prints 123 in hex
std::cout << 73 << "\n"; // problem! prints 73 in hex..
}
答案 5 :(得分:3)
您可以在stdout缓冲区周围创建另一个包装器:
#include <iostream>
#include <iomanip>
int main() {
int x = 76;
std::ostream hexcout (std::cout.rdbuf());
hexcout << std::hex;
std::cout << x << "\n"; // still "76"
hexcout << x << "\n"; // "4c"
}
在一个功能中:
void print(std::ostream& os) {
std::ostream copy (os.rdbuf());
copy << std::hex;
copy << 123;
}
当然,如果性能是一个问题,这会有点贵,因为它复制整个ios
对象(但不是缓冲区),包括你付出的一些东西,但不太可能使用诸如语言环境。
否则我觉得如果您要使用.flags()
,那么最好保持一致并使用.setf()
而不是<<
语法(纯风格问题)。
void print(std::ostream& os) {
std::ios::fmtflags os_flags (os.flags());
os.setf(std::ios::hex);
os << 123;
os.flags(os_flags);
}
正如其他人所说,你可以把上面的内容(和.precision()
和.fill()
,但通常不是一个通常不会被修改并且更重的区域设置和与文字相关的内容为方便起见,使其异常安全;构造函数应该接受std::ios&
。
答案 6 :(得分:2)
在大多数情况下,C ++ 20 std::format
将是保存还原的绝佳选择
一旦您可以使用它,例如能够简单地写成十六进制:
#include <format>
#include <string>
int main() {
std::cout << std::format("{x} {#x} {}\n", 16, 17, 18);
}
预期输出:
10 0x11 18
因此,这将完全克服修改std::cout
状态的疯狂。
更多信息,请访问:C++ cout hex values?
答案 7 :(得分:0)
我想概括一下qbert220的答案:
#include <ios>
class IoStreamFlagsRestorer
{
public:
IoStreamFlagsRestorer(std::ios_base & ioStream)
: ioStream_(ioStream)
, flags_(ioStream_.flags())
{
}
~IoStreamFlagsRestorer()
{
ioStream_.flags(flags_);
}
private:
std::ios_base & ioStream_;
std::ios_base::fmtflags const flags_;
};
这同样适用于输入流和其他输入流。
PS:我本想对以上答案做一个简单的评论,但是由于缺少声誉,stackoverflow不允许我这样做。因此,让我在这里将答案弄得一团糟,而不是简单的评论...
答案 8 :(得分:0)
不是将格式注入 cout,而是采用 <<
方式,采用 setf
和 unsetf
可能是一个更简洁的解决方案。
void printHex(std::ostream& x){
x.setf(std::ios::hex, std::ios::basefield);
x << 123;
x.unsetf(std::ios::basefield);
}
ios_base 命名空间也能正常工作
void printHex(std::ostream& x){
x.setf(std::ios_base::hex, std::ios_base::basefield);
x << 123;
x.unsetf(std::ios_base::basefield);
}