提问人:UltraInstinct 提问时间:2/16/2010 最后编辑:leiycUltraInstinct 更新时间:9/4/2022 访问量:62365
操作 std::cout 后恢复状态
Restore the state of std::cout after manipulating it
问:
假设我有一个这样的代码:
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
谢谢。
答:
Boost IO Stream State Saver 似乎正是您所需要的。:-)
基于代码片段的示例:
void printHex(std::ostream& x) {
boost::io::ios_flags_saver ifs(x);
x << std::hex << 123;
}
评论
ios_flags_saver
ios_flags_saver
您需要或在需要时:#include <iostream>
#include <ios>
std::ios_base::fmtflags f( cout.flags() );
//Your code here...
cout.flags( f );
您可以将它们放在函数的开头和结尾,或者查看此答案,了解如何将其与 RAII 一起使用。
评论
稍作修改,使输出更具可读性:
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..
}
我使用此答案中的示例代码创建了一个 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;
}
评论
请注意,此处提供的答案不会恢复 的完整状态。例如,即使在调用后也会“粘”.更好的解决方案是使用:std::cout
std::setfill
.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
评论
std::ios
NULL
std::stringstream
rdbuf
std::ios
state.copyfmt
oldState
rdbuf
std::ios
struct : std::streambuf {} sbuf; std::ios oldState(&sbuf);
您可以在 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&
评论
std::stringstream
std:ostream
std:stringstream
std:stringbuf
std::streambuf
std::cout.rdbuf()
我想从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不允许我这样做。因此,让我在这里把答案弄得一团糟,而不是简单的评论......
在大多数情况下,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
现有库在获得官方支持之前实现它: https://github.com/fmtlib/fmt 在 Ubuntu 22.04 上安装:fmt
sudo apt install libfmt-dev
修改源以替换:
<format>
跟<fmt/core.h>
std::format
自fmt::format
主 .cpp
#include <iostream>
#include <fmt/core.h>
int main() {
std::cout << fmt::format("{:x} {:#x} {}\n", 16, 17, 18);
}
并使用以下命令进行编译和运行:
g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out
输出:
10 0x11 18
评论
与其将格式注入 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);
}
评论