CPP/Cpp module

[cpp개념공부]문자열 스트림

뜨거운 개발자 2023. 3. 6. 14:07

표준 정보는 여기서 봅시다.

https://cplusplus.com/reference/sstream/stringstream/

https://en.cppreference.com/w/cpp/io/basic_stringstream

문자열 스트림 (std::stringstream)

마치 문자열을 하나의 스트림이라 생각하게 해주는 가상화 장치

sstream에는 std::istringstream 이 정의되어 있는데 이는 문자열을 하나의 스트림으로 생각하게 해주는 가상화 장치 입니다.

std::istringstream 을 통해서 123 을 읽어낼 수 있습니다.

이를 활용하면 atoi 와 같은 함수를 사용할 필요 없이 간편하게 문자열에서 숫자로 변환하는 함수를 만들 수 있습니다.

스트림에서 데이터를 가져오는 방법

스트림에서 데이터를 갖고 오는 방법은 여러가지가 있다.

스트림의 텍스트 모드

스트림 상의 데이터를 바이트 단위로 읽어서 Whitespace 단위로 끊은 뒤에 원하는 타입으로 읽어오는 방법.

std::stringstream :스트림의 텍스트 모드를 이용하면 단순히 nan, inf와 같은 입력을 받아도 이를 명확하게 실수로 변환할 수 있고, 그 외의 문자로 입력을 받은 경우에는 수로 인식을 하지 못하여 이를 따로 처리하는 것도 가능하다.

std::istream_iterator<T>를 사용하면 Whitespace를 손실하게 되지만, 여러 개의 입력 값을 원하는 타입으로 얻어낼 수 있다.

스트림의 바이너리 모드

스트림에서 데이터를 바이너리 단위로 읽어서 문자 타입으로 읽을 수도 있다.

istreambuf_iterator<T>는 스트림의 파일 버퍼에 직접 접근할 수 있고, 텍스트 모드와 달리 바이트 단위의 변환이 없기 때문에 istream_iterator<T>보다 빠르게 작업을 처리할 수 있다.

사용 방법

  1. clear() : 스트림을 지웁니다.
  2. str() : 콘텐츠가 스트림에 있는 문자열 개체를 가져오고 설정합니다.
  3. operator << : stringstream 개체에 문자열을 추가합니다.
  4. 연산자 >> : tringstream 객체에서 무언가를 읽습니다.

예시1 (string to int)

#include <iostream>
#include <sstream>

int main() {
  std::istringstream ss("123");
  int x;
  ss >> x;

  std::cout << "입력 받은 데이터 :: " << x << std::endl;

  return 0;
}

예시2 (int to string)

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

std::string to_str(int x) {
  std::ostringstream ss;
  ss << x;

  return ss.str();.//이건 int형을 문자열로 쉽게 바꿀수 있다. 신기하다.!!!
}
int main() {
  std::cout << "문자열로 변환:: 1 + 2 = " << to_str(1 + 2) << std::endl;

  return 0;
}

예시 3 (Dex to Hex)

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    int i = 942;
    stringstream ss;
    ss << hex << i;
    string res = ss.str();
    cout << "0x" << res << endl; // this will print 0x3ae
    return 0;
}

예시 4(Hex to Dex)

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    string hexStr = "0x3ae";
    unsigned int x;
    stringstream ss;
    ss << std::hex << hexStr;
    ss >> x;
    cout << x << endl; // this will print 942
    return 0;
}

파일 스트림에서 버퍼를 효율적으로 사용하는 방법

std::ifstream 에서 파일을 읽어올 때 iterator은 파일 탐색을 기반으로 하기 때문에 파일 디스크의 버퍼를 계속해서 불러와야만 한다. 이는 상당히 큰 단점으로 작용한다. 따라서 만약 여러번 파일을 읽어오는 경우에는 파일 스트림이 가지고 있는 버퍼의 내용을 메모리 상으로 옮겨서 작업을 해야한다.

이것이 가능하게 해주는 것이 std::stringstream이다.

std::ifstream의 데이터를 std::stringstream 으로 가져올 때는 itreator(<< , >>) 를 활용해도 된다.

그외의 방법으로는 스트림간의 데이터 이동에서만 사용할 수 있는 방법인데 rdbuf라는 멤버 함수를 이용하면 한번에 데이터를 옮기는 것이 가능하다.

rdbuf함수

basic_filebuf라는 객체의 주소를 반환하는데 이를 << 라는 삽입 연산자와 함께 사용하면 한 번에 입력할 수 있다.

#include <iostream>     // std::cout
#include <fstream>      // std::filebuf, std::ifstream
#include <sstream>
int main () {
	std::ifstream ifs ("test.txt", std::ifstream::binary);

	// get pointer to associated buffer object
	std::filebuf* pbuf = ifs.rdbuf();
	//std::cout << pbuf;
	std::stringstream ss ;
	ss	<< pbuf;
	int a ;
	ss >> a;
	std::cout << a;
}

예시 코드를 보면 이해가 쉬울 것 같다. 직접 조작해보면서 익히도록 하자.

stream으로 입력을 받을 때 문제점

overflow와 underflow에 대해서 제대로 대응하기 어렵다. 만약 그것에 대해서 대응하고 싶다면 std::stoi, std::stof, std::stod 등 함수들이 그 역활을 수행 할 수 있을 것이다.

소수점 아래 리터럴 표기가 있는 경우 처리

기본적으로 stream data로 문자를 받을 때 1..1… 이런식으로 입력을 받게 되면, 정상적으로 처리를 하지 못하는 단점이 있었다. 따라서 이 문제를 해결하려면 std::atof 혹은 std::strtod 함수를 사용하면 된다.

std::atof vs std::strtod

atod함수는 double표현범위를 벗어나면 undefine되어있으나, std::strtod같은 경우, 범위를 벗어나도 errno 역시 ERANGE로 설정이 되고 , HUGE_VAL을 반환해서 조금 std::strtod함수가 더 좋은 함수임을 알 수가 있다.

std::isnan & std::isinf

스트림에서 뽑아낸 형변환이 nan인지 inf인지도 구분하려면 <cmath>의 std::isnan, std::isinf를 이용하여 알아낼 수 있다. cpprefence 혹은 cplusplus에서 검색해보면 두 함수가 C++11인 것을 볼 수 있는데, C99 버전의 isnan과 isinf가 <cmath>에도 존재합니다. 두 함수는 모두 매크로 함수를 호출하는 함수이다. C99 버전의 <cmath>는 C++98에 완전한 호환성을 가진다.

std::showpos

부호를 출력해주는 설정이다.

std::cout << std::showpos << 1 

이렇게 입력을 해보면, +1 이 나오는 것을 알 수가 있다.

728x90