首页 文章

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

提问于
浏览
80

假设我有这样的代码:

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

谢谢 .

6 回答

  • 55

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

    std::ios_base::fmtflags f( cout.flags() );
    
    //Your code here...
    
    cout.flags( f );
    

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

  • 35

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

    基于代码段的示例:

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

    请注意,此处提供的答案不会恢复 std::cout 的完整状态 . 例如,即使在调用 .flags() 之后, std::setfill 也将"stick" . 更好的解决方案是使用 .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

    我使用此答案中的示例代码创建了一个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;
    }
    
  • 18

    通过一些修改使输出更具可读性:

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

    您可以在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 对象(但不是缓冲区),包括你付出但不太可能使用的一些东西,比如locale .

    否则我觉得如果你要使用 .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& .

相关问题