std :: cout 조작 후 상태 복원


105

다음과 같은 코드가 있다고 가정합니다.

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::boolalphastd::noboolalpha..)?

감사.


나는 hex가 다음 시프트 아웃 작업을 위해서만 지속된다고 생각합니다. 변경 사항은 조작자를 사용하는 대신 수동으로 형식 플래그를 변경하는 경우에만 영구적입니다.
Billy ONeal 2010

4
@BillyONeal : 아니요, 조작기를 사용하는 것은 형식 플래그를 수동으로 변경하는 것과 동일한 효과가 있습니다. :-P
Chris Jester-Young

3
Covertiy 결과로 인해 Ostream 형식을 복원하지 않음 (STREAM_FORMAT_STATE) , Coverity 결과 : ostream 형식을 복원하지 않음 (STREAM_FORMAT_STATE) 을 참조하십시오 .
jww

비슷한 작업을했습니다. Code Review : Use a standard stream, and restore its settings after after에 대한 질문을 참조하십시오 .
Toby Speight

1
이 질문은 iostream이 stdio보다 낫지 않은 이유에 대한 완벽한 예입니다. 영구적이지 않은 iomanip으로 인해 두 가지 불쾌한 버그를 발견했습니다.
fuujuhi

답변:


97

필요 #include <iostream>하거나 #include <ios>필요할 때 :

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

//Your code here...

cout.flags( f );

함수의 시작과 끝에 넣거나 RAII 와 함께 사용하는 방법에 대한 이 답변 을 확인할 수 있습니다 .


5
@ ChrisJester-Young, 실제로 좋은 C ++는 RAII입니다. 특히 이와 같은 경우에 그렇습니다!
Alexis Wilke 2015

4
@Alexis 100 % 동의합니다. 내 답변을 참조하십시오 (Boost IO Stream State Saver). :-)
Chris Jester-Young

3
이것은 예외로부터 안전하지 않습니다.
einpoklum

2
플래그 외에 스트림 상태에 더 많은 것이 있습니다.
jww

3
형식을 스트림에 푸시하지 않으면 문제를 피할 수 있습니다. 임시 이제 stringstream 변수에 형식과 데이터를 밀어 후 인쇄
마크 Sherred

63

부스트 IO 스트림 주립 보호기는 당신이 필요로 정확히 보인다. :-)

코드 스 니펫을 기반으로 한 예 :

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

1
여기에는 ios_flags_saver기본적으로 @StefanKendall의 대답과 같이 플래그를 저장하고 설정하는 마법이 없습니다 .
einpoklum

15
@einpoklum 그러나 다른 답변과 달리 예외 안전합니다. ;-)
Chris Jester-Young

2
플래그 외에 스트림 상태에 더 많은 것이 있습니다.
jww

4
@jww IO Stream State Saver 라이브러리에는 스트림 상태의 다른 부분을 저장하기위한 여러 클래스 ios_flags_saver가 있습니다.
크리스 광대 - 젊은

3
검토되고 잘 테스트 된 라이브러리를 사용하는 대신 모든 사소한 것을 스스로 다시 구현하고 유지 관리 할 가치가 있다고 생각한다면 ...
jupp0r

45

여기에 제시된 답변은의 전체 상태를 복원하지 않습니다 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

내 원래 질문에 대한 답변은 몇 년 전이지만이 답변은 큰 도움이됩니다. :-)
UltraInstinct

2
@UltraInstinct 더 나은 솔루션 인 것 같습니다.이 경우 대신 허용되는 답변으로 만들 수 있으며 그럴 수도 있습니다.
underscore_d

스트림에 대해 예외가 활성화 된 경우 몇 가지 이유로 예외가 발생합니다. coliru.stacked-crooked.com/a/2a4ce6f5d3d8925b
anton_rh dec

1
rdbuf 가 있기 때문에 std::ios항상 나쁜 상태 인 것 같습니다 NULL. 따라서 예외가 활성화 된 상태를 설정하면 잘못된 상태로 인해 예외가 발생합니다. 솔루션 : 1) . 대신 set std::stringstream와 함께 일부 클래스 (예 :)를 사용하십시오 . 2) 예외 상태를 로컬 변수에 별도로 저장하고 이전에 비활성화 한 다음 변수에서 예외를 복원합니다 ( 예외가 비활성화 된 상태를 복원 한 후 다시 수행 ). 3) 다음 과 같이 설정 합니다 .rdbufstd::iosstate.copyfmtoldStaterdbufstd::iosstruct : std::streambuf {} sbuf; std::ios oldState(&sbuf);
anton_rh

22

이 답변의 예제 코드를 사용하여 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;
}

2
훌륭합니다. 누군가가 던지면 스트림에 올바른 플래그가 있습니다.
Alexis Wilke

4
플래그 외에 스트림 상태에 더 많은 것이 있습니다.
jww

1
C ++에서 try / finally를 허용했으면합니다. 이것은 RAII가 작동하는 훌륭한 예이지만 마침내 더 간단했을 것입니다.
무역 아이디어 필립

2
프로젝트가 적어도 약간 정상이라면 Boost가 있으며 이러한 목적을 위해 상태 보호기 가 함께 제공됩니다 .
얀 허덱

9

출력을 더 읽기 쉽게 만들기 위해 약간의 수정이 필요합니다.

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

9

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


좋은 점 [+]이지만 Mark Sherred가 지적한std::stringstream 대로 포맷팅 부분 에 사용 하는 것을 기억합니다 .
Wolf

@Wolf 나는 당신의 요점을 확실하지 않습니다. An std::stringstream 은입니다 . 단 std:ostream, 하나를 사용하면 추가 중간 버퍼가 도입됩니다.
n.caillou

물론 둘 다 출력 형식화에 대한 유효한 접근 방식이며 둘 다 스트림 개체를 도입하며 설명하는 것은 나에게 새로운 것입니다. 나는 지금 장단점에 대해 생각해야한다. 그러나 깨달음을주는 질문이 있습니다. (내 말은 스트림 복사 변형을 의미합니다)
Wolf

1
버퍼를 복사하는 것은 종종 의미가 없기 때문에 스트림을 복사 할 수 없습니다 (예 : stdout). 그러나 동일한 버퍼에 대해 여러 스트림 객체를 가질 수 있으며, 이것이이 답변이 제안하는 것입니다. std:stringstream의지는 자신의 독립적 인 std:stringbuf( std::streambuf파생물) 을 생성하는 반면에 부어 std::cout.rdbuf()
넣어야합니다

설명해 주셔서 감사합니다.
Wolf

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

이것은 입력 스트림 및 기타에서도 작동합니다.

추신 : 나는 이것을 단순히 위의 답변에 대한 주석으로 만들고 싶었지만, 평판이 없어서 그렇게 할 수는 없습니다. 따라서 간단한 설명 대신 여기에 답을 혼란스럽게 만드십시오 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.