操作后恢复std :: cout的状态

Ult*_*nct 91 c++ iostream

假设我有这样的代码:

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..
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,在从函数返回后,是否有任何方法可以将cout的状态"恢复"到原来的状态?(有点像std :: boolalpha和std :: noboolalpha ..)?

谢谢.

Ste*_*all 82

您需要#include <iostream>#include <ios>在需要时:

std::ios_base::fmtflags f( cout.flags() );

//Your code here...

cout.flags( f );
Run Code Online (Sandbox Code Playgroud)

您可以将这些放在函数的开头和结尾,或者查看关于如何在RAII中使用它的答案.

  • 谷歌搜索给了我这个 (14认同)
  • @ChrisJester-Young,实际上很好的C++是RAII,特别是在像这样的情况下! (5认同)
  • @Alexis我100%同意.请参阅我的答案(Boost IO Stream State Saver).:-) (4认同)
  • 这不是例外安全的. (3认同)
  • 除了标志之外,流状态还有更多内容。 (3认同)
  • 您可以通过不将格式推送到流中来避免此问题。将格式和数据压入一个临时的stringstream变量,然后打印 (2认同)

Chr*_*ung 60

升压IO流状态节电器似乎正是你需要的.:-)

基于代码段的示例:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}
Run Code Online (Sandbox Code Playgroud)

  • @einpoklum但与其他答案不同,它是异常安全的.;-) (14认同)
  • @jww IO Stream State Saver库有多个类,用于保存流状态的不同部分,其中`ios_flags_saver`只是一个. (4认同)
  • 如果您认为值得自己重新实现和维护每一件小事,而不是使用经过评估,经过良好测试的库... (3认同)
  • 除了标志之外,流状态还有更多。 (2认同)

rr-*_*rr- 40

请注意,此处提供的答案不会恢复完整状态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;
Run Code Online (Sandbox Code Playgroud)

将打印:

case closed
Run Code Online (Sandbox Code Playgroud)

而不是:

case closed0000
Run Code Online (Sandbox Code Playgroud)

  • @UltraInstinct它似乎是一个*更好的*解决方案,在这种情况下,您可以并且可能应该将其作为接受的答案. (2认同)

qbe*_*220 21

我使用此答案中的示例代码创建了一个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;
};
Run Code Online (Sandbox Code Playgroud)

然后,只要您想保存当前标志状态,就可以通过创建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;
}
Run Code Online (Sandbox Code Playgroud)

  • 除了旗帜之外,还有更多的流状态. (4认同)
  • 很好,如果有人投掷,你仍然在你的流中得到正确的标志. (2认同)
  • 如果您的项目至少有点理智,那么您有 Boost 并且为此目的带有 [state savers](https://www.boost.org/libs/io/doc/ios_state.html)。 (2认同)

wha*_*cko 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..
}
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 6

std::format在大多数情况下,C++20将是保存恢复的更好选择

一旦你可以使用它,你就可以简单地编写十六进制:

#include <format>
#include <string>

int main() {
    std::cout << std::format("{x} {#x} {}\n", 16, 17, 18);
}
Run Code Online (Sandbox Code Playgroud)

预期输出:

10 0x11 18
Run Code Online (Sandbox Code Playgroud)

因此,这将完全克服修改std::cout状态的疯狂。

更多信息请访问:C++ cout hex values?

  • 很高兴知道,但截至 2021 年 4 月,编译器不支持它,即使标准已经存在,也许在这个答案中值得一提。 (3认同)

n.c*_*lou 5

您可以在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"
}
Run Code Online (Sandbox Code Playgroud)

在函数中:

void print(std::ostream& os) {
    std::ostream copy (os.rdbuf());
    copy << std::hex;
    copy << 123;
}
Run Code Online (Sandbox Code Playgroud)

当然,如果性能是一个问题,那会贵一点,因为它会复制整个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);
}
Run Code Online (Sandbox Code Playgroud)

正如其他人所说,您可以将上述内容(和.precision().fill(),但通常不包括与语言环境和单词相关的东西,这些东西通常不会被修改且较重)放在一个类中,以方便使用并使其异常安全;构造函数应该接受std::ios&

  • 您无法复制流,因为复制缓冲区通常没有意义(例如标准输出)。但是,同一个缓冲区可以有多个流对象,这就是这个答案建议要做的。而 `std:stringstream` 将创建自己独立的 `std:stringbuf` (`std::streambuf` 派生体),然后需要将其倒入 `std::cout.rdbuf()` 中 (2认同)