sstream
은 string stream
이라고 불리며, 메모리 버퍼에 문자열을 저장해두고 필요할 때 사용할 수 있는 라이브러리이다. 보통 문자열 파싱 또는 다양한 타입을 받아 문자열로 변환할 때 사용한다.
(stream과 buffer의 차이)
위에서 이야기한대로 내부 구현을 살펴보면 basic_streambuf
을 상속받은 _Stringbuffer
가 존재한다는 것을 알 수 있다.
// sstream.h
template <class _Elem, class _Traits, class _Alloc>
class basic_stringstream: public basic_iostream<_Elem, _Traits> {
public:
...
private:
_Mysb _Stringbuffer;
};
sstream
에는 입력을 담당하는 istringstream
객체와 출력을 담당하는 ostringstream
객체가 존재한다.
istringstream
은 문자열 포맷을 파싱할 때 사용한다.
istringstream
에서 >>
연산자를 사용하게 될 경우, 메모리(buffer)로부터 한 단어를 입력받는다. 입력받을 때의 자료형은 입력받을 변수의 자료형을 따르게 된다.
(한 단어의 기준은 개행(\n
) 또는 공백)
즉, istringstream은 아래와 같이 사용할 수 있다.
#include <iostream>
#include <sstream>
int main() {
std::istringstream iss("123 ab 3.14");
int a;
std::string b;
double c;
// istringstream 버퍼에 담긴 순서대로 123, ab, 3.14를 추출함.
// 추출된 문자열은 대입할 변수의 타입에 맞게 저장된다.
iss >> a >> b >> c;
std::cout << a << "\n" << b << "\n" << c;
// output:
// 123
// ab
// 3.14
return 0;
}
// https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt'
// 입력받을 때 자료형이 상관없는 이유에는 엄청난 오버로딩이 숨겨져 있었다...
basic_istream& __CLR_OR_THIS_CALL operator>>(unsigned short& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(int& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(unsigned int& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(long& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(unsigned long& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(long long& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(unsigned long long& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(float& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(double& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(long double& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(void*& _Val) { ... }
basic_istream& __CLR_OR_THIS_CALL operator>>(_Mysb* _Strbuf) { ... }
istringstream
의 >>
연산자는 가져올 문자열이 있다면 true
, 더 이상 가져올 문자열이 없다면 false
를 반환한다. 이를 활용하여 문자열에서 단어들을 쉽게 추출할 수 있다.
// https://www.acmicpc.net/problem/1152
// sstream을 활용한 단어 개수 세기
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
string input_str;
getline(cin, input_str);
istringstream iss(input_str);
int cnt = 0;
string word;
while (iss >> word) { // 더 이상 가져올게 없을 경우, false가 되어 반복문 종료
cout << word << "\n"; // iss에 저장된 문자열에서 한 단어씩 꺼내어 word에 저장
cnt++;
}
cout << cnt << "\n";
return 0;
}
sentry
클래스는 basic_istring
안에 있는 중첩 클래스로, 해당 클래스 덕분에 while (iss >> word)
가 가능하게 된 것이다.
istringstream
의 operator>>
가 호출되면, 함수 내부에 sentry
객체가 생성되고 이 sentry
객체는 스트림의 상태를 체크한다.
sentry
객체는 true
를 반환)sentry
객체는 false
를 반환)// istream.h
// 자세한 설명은 아래의 링크를 참고해주세요
// https://cplusplus.com/reference/istream/istream/sentry/
class sentry : public _Sentry_base {
public:
// _Noskip은 공백을 skip할 건지에 대한 변수로 true일 때, 공백을 skip 하지 않는다.
// 다음과 같이 사용가능하다 : iss >> noskipws >> word
explicit __CLR_OR_THIS_CALL sentry(basic_istream& _Istr, bool _Noskip = false)
: _Sentry_base(_Istr), _Ok(_Sentry_base::_Myistr._Ipfx(_Noskip)) {}
// 이 부분이 true/false값을 반환해주는 부분이다.
explicit __CLR_OR_THIS_CALL operator bool() const {
return _Ok;
}
__CLR_OR_THIS_CALL sentry(const sentry&) = delete;
sentry& __CLR_OR_THIS_CALL operator=(const sentry&) = delete;
private:
bool _Ok; // true if _Ipfx succeeded at construction
};
ostringstream
은 문자열 format을 조합하여 저장할 때 사용한다.
ostringstream
에서 <<
연산자를 사용하게 될 경우, 메모리(buffer)에 저장할 내용을 문자열로 저장한다. 여기서 내용은 어느 type이든 상관없이 문자열로 저장이 된다.
ostringstream
은 아래와 같이 사용할 수 있다.
#include <iostream>
#include <sstream>
int main() {
std::ostringstream oss;
int student_cnt = 10;
// 아래에 적힌 '문자열' + 'int' + '문자열' 을 oss의 버퍼에 저장한다.
// 물론 다른 타입도 가능하다.
oss << "현재 등록된 학생 수는 " << student_cnt << "명 입니다.";
std::cout << oss.str(); // 버퍼에 저장된 문자열을 꺼내기 위해 .str() 사용
// output:
// 현재 등록된 학생 수는 10명 입니다.
return 0;
}
// https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt
// istringstream과 마찬가지로 ostringstream에도 오버로딩이...
basic_ostream& operator<<(bool value);
basic_ostream& operator<<(long value);
basic_ostream& operator<<(unsigned long value);
basic_ostream& operator<<(long long value);
basic_ostream& operator<<(unsigned long long value);
basic_ostream& operator<<(double value );
basic_ostream& operator<<(long double value );
...
필자는 ostringstream
을 많이 사용해보지 않았기 때문에 다른 블로그에서 찾은 내용을 적어놓았다.
C 언어에서는 문자열을 다루기 위해 buffer + sprintf
같은 함수를 많이 사용했다고 한다.
#include <iostrema>
#include <sstream>
#include <string>
using namespace std;
int main()
{
int n = 10;
char buf[20];
sprintf(buf, "이 제품의 가격은 %d 입니다.", n);
// buf 출력
}
0xd00d00 blog's content
Hope this help you :)
original content : https://0xd00d00.github.io/2021/08/21/cpp_sstream.html
그러나 이 방식의 경우, n
의 자리수가 크게 되면, buf
의 크기인 20을 넘는 buffer overflow가 발생할 수 있다. 반면, ostringstream
의 메모리 크기는 string
을 wrapping해 만들었기 때문에 string
의 max 사이즈를 가지고 있다고 한다.
(ostringstream
을 사용하면 메모리 크기에 대해 고려할 필요가 없다)
표준 C++ string만 사용하지 않고 문자열 스트림을 함께 활용하면 데이터를 읽거나 쓸 지점(curretn position)을 알 수 있어서 좋습니다. 또한 문자열 스트림은 다양한 매니퓰레이터와 로케일을 지원하므로 string보다 포맷을 보다 융통성있게 다룰 수 있습니다.