[C++] stringstream을 while문의 조건으로 사용할 때 주의할 점
코딩 테스트 문제를 풀다 보면 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 형변환 결과가 다르다.
문서를 보자.
여기서 주목할 부분은, 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를 처음 읽은 후, 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
씹어먹는 C++ - <7 - 2. C++ 에서 파일 입출력 - std::ifstream. std::ofstream, std::stringstream>
modoocode.com