[C++] stringstream (split)

alirz-pixel·2023년 8월 1일
1

C, C++

목록 보기
6/6

sstream

sstreamstring 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은 문자열 포맷을 파싱할 때 사용한다.

operator>>

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

istream::sentry

sentry 클래스는 basic_istring 안에 있는 중첩 클래스로, 해당 클래스 덕분에 while (iss >> word)가 가능하게 된 것이다.

istringstreamoperator>>가 호출되면, 함수 내부에 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

ostringstream은 문자열 format을 조합하여 저장할 때 사용한다.

operator<<

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을 많이 사용해보지 않았기 때문에 다른 블로그에서 찾은 내용을 적어놓았다.

사용이유 1.

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을 사용하면 메모리 크기에 대해 고려할 필요가 없다)

사용이유 2.

표준 C++ string만 사용하지 않고 문자열 스트림을 함께 활용하면 데이터를 읽거나 쓸 지점(curretn position)을 알 수 있어서 좋습니다. 또한 문자열 스트림은 다양한 매니퓰레이터와 로케일을 지원하므로 string보다 포맷을 보다 융통성있게 다룰 수 있습니다.



reference

0개의 댓글