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

问题描述 投票:85回答:6

假设我有这样的代码:

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 ..)?

谢谢。

c++ iostream
6个回答
78
投票

你需要#include <iostream>#include <ios>然后需要:

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

//Your code here...

cout.flags( f );

您可以将这些放在函数的开头和结尾,或者查看this answer如何在RAII中使用它。


55
投票

Boost IO Stream State Saver似乎正是你所需要的。 :-)

基于代码段的示例:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}

37
投票

请注意,此处提供的答案不会恢复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

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;
}

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..
}

4
投票

您可以在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&

© www.soinside.com 2019 - 2024. All rights reserved.