Study/C & C++

[C++] stringstream을 while문의 조건으로 사용할 때 주의할 점

게임 만드는 나무꾼 2022. 10. 13. 14:13

코딩 테스트 문제를 풀다 보면 stringstream으로 값을 추출할 때가 많다.

공백이나 특정 문자 기준으로 잘라내기 위해 while문과 stringstream 객체를 조합하여 사용할 경우가 많다.

stringstream객체를 while문의 조건으로 사용할 때, 주의하지 않으면 분명 다 읽었는데 반복을 한 번 더 돌거나 하는 문제가 생길 수 있다.

 

아래 코드는 공백으로 구분된 문자열을 받아, 공백을 지우고 출력하는 코드이다.

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main()
{
    string input;
    
    getline(cin, input); // 개행문자 전까지 한 줄 읽음

    stringstream ss(input);

    string buf;
    while (ss)
    {
        // 공백 단위로 잘라서 출력
        ss >> buf;
        cout << buf;
    }

    return 0;
}

출력 결과는 다음과 같았다.

Anger가 두 번 출력되는 현상을 볼 수 있다.

이는 마지막 Anger라는 문자를 읽고, buf가 초기화가 되지 않은 상태로 한 번 더 반복했다는 뜻이다.

 

현재 while문의 조건으로 stringstream객체 자체를 넣어주고 있는데, 이 조건이 문제라고 할 수 있다.

while문의 조건으로 stringstream이 들어가면, stringstream은 오버로딩된 bool()연산자에 의해 bool type으로 형변환된다.

stringstream은 iostate flag에 따라 bool type 형변환 결과가 다르다.

문서를 보자.

출처 : https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool

여기서 주목할 부분은, eof()가 true일 때의 bool 형변환 결과이다.

eof()가 true일 때도,  bool type 형변환 결과가 true인 것을 볼 수 있다.

위 코드에서 마지막 anger문자열을 읽으면, stringstream의 eof flag는 true가 된다.

그리고 다시 while의 조건문으로 돌아가며, stringstream객체는 flag 상태에 의해 true를 반환하기에 한 번 더 반복하게 된다.

그리고 한 번 더 >> 연산자로 추출을 시도하면 fail bit가 true로 전환, while문의 조건을 만족하지 못해 그제야 반복이 끝나게 되는 것이다.

확인을 위해 반복할때마다 flag 상태를 출력해보겠다.

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main()
{
    string input;
    
    getline(cin, input); // 개행문자 전까지 한 줄 읽음

    stringstream ss(input);

    string buf;
    while (ss)
    {
        // 공백 단위로 잘라서 출력
        ss >> buf;
        cout << buf << '\n';

        cout << "eof : " << std::boolalpha << ss.eof() << '\t';
        cout << "fail : " << std::boolalpha << ss.fail() << '\n';
        cout << "=======================================\n";
    }

    return 0;
}

Anger를 읽을 때의 flag상태를 보자

Anger를 처음 읽은 후, eof가 true상태가 되고, 한 번 더 읽은 후에야 fail이 true로 전환되는 모습을 볼 수 있다.

 

결론

while (ss >> buf)
{
    // 공백 단위로 잘라서 출력
    cout << buf << '\n';
}

이런 식으로 ss >> buf 를 조건문 자체에 넣으면, >> operator의 반환값으로 istream&이 반환된다.

이 istream&은 >> 연산이 실행된 이후이므로, failbit가 켜지자마자 조건을 충족하지 않아 반복을 끝내게 된다.

 

참고로, 절대 while문 조건에 ss.eof()를 넣으면 안 된다.

eof()는 읽기가 안전하다는 것을 보장하지 않기 때문이다.

읽기 도중 알 수 없는 이유로 badbit나 failbit가 켜지는 경우를 생각해보면 된다.


https://stackoverflow.com/questions/20572203/detect-when-at-the-end-of-a-stringstream

 

Detect when at the end of a stringstream

I am trying to write a function which will detect when I'm almost at the end of a stringstream, but it doesn't seem to work. Here's some code: std::string path(1.2.3); int number; std::stringstr...

stackoverflow.com

https://stackoverflow.com/questions/47876920/stringstream-c-while-loop

 

Stringstream c++ while loop

Program finds integeres between commas like "2,33,5" -> 2 33 5. The problem is why is it working if I put for example string like "0,12,4". shouldn't the stringstream put 0 into tmp so the loop was...

stackoverflow.com

https://modoocode.com/215

 

씹어먹는 C++ - <7 - 2. C++ 에서 파일 입출력 - std::ifstream. std::ofstream, std::stringstream>

 

modoocode.com