Menu

C++ 문자열 스트림: stringstream으로 파싱하고 만들기

std::stringstream, istringstream, ostringstream을 사용해 텍스트를 파싱하고, 공백을 기준으로 문자열을 나누고, 문자열과 숫자를 서로 변환하고, 메모리에서 서식 있는 문자열을 만드는 방법.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

문자열 안에서 동작하는 스트림

여러분은 이미 cincout을 알고 있습니다. 키보드와 화면에 연결된 스트림이죠. 문자열 스트림도 같은 발상이지만, 데이터가 대신 메모리 안의 std::string에 들어 있습니다. <<로 쓰고 >>로 값을 꺼내는 방식은 콘솔 스트림과 정확히 같지만, 터미널에는 아무것도 닿지 않습니다.

이 하나의 발상이 일상적인 두 가지 문제를 해결합니다. 텍스트를 타입이 있는 값으로 파싱하는 것과, 서로 다른 조각들로 서식 있는 문자열을 만드는 것입니다. 모든 것은 <sstream> 헤더에 있고 세 가지 형태로 제공됩니다.

  • istringstream - 읽기 전용, 입력 파싱용.
  • ostringstream - 쓰기 전용, 출력 생성용.
  • stringstream - 양방향.

ss.str()은 지금까지 쓴 모든 내용을 일반 string으로 반환합니다. << 연산자는 cout이 하는 것과 같은 int에서 텍스트로의 서식 변환을 했고, 여러분은 그것을 출력하는 대신 결과를 붙잡았을 뿐입니다.

파싱: 타입이 있는 값 꺼내기

진정한 힘은 >>에서 나타납니다. 스트림에 텍스트를 주면 추출이 각 토큰을 여러분이 요청한 타입으로 변환하면서 그 사이의 공백을 건너뜁니다. 이것은 한 줄을 여러 개의 타입이 있는 필드로 쪼개는 깔끔한 방법입니다.

>>는 다음 공백까지 읽고 변환합니다. "Ada"string으로, "36"int로, "5.5"double로요. 여기서 istringstream에 주목하세요. 입력 전용 타입은 여러분이 파싱만 하고 있음을 분명히 합니다.

문자열을 숫자로 변환하기 (안전하게)

자주 하는 일은 "42" 같은 하나의 문자열을 int로 바꾸는 것입니다. stringstream은 그 일을 해 주고, 게다가 텍스트가 실제로 유효한 숫자였는지도 알려 줍니다. 이는 atoi가 결코 하지 않는 일입니다.

여기서 ss >> value123을 읽고 a에서 멈춘 뒤 성공합니다. 그러니 중요하다면 항상 문자열 전체를 검증하세요. 견고한 검사는 숫자를 읽은 다음 의미 있는 것이 남아 있지 않은지 확인하는 것입니다. 단순한 단일 값 변환에는 std::stoi, std::stod와 그 동료들(형 변환과 함께 다룸)이 더 짧습니다. 하나의 문자열이 여러 종류의 값을 담고 있을 때 stringstream을 쓰세요.

구분자로 문자열 나누기

텍스트를 나누는 것은 가장 많이 검색되는 문자열 스트림 작업입니다. 스트림을 std::getline과 결합하세요. getline은 줄바꿈뿐 아니라 어떤 구분 문자까지든 읽을 수 있습니다.

while (getline(...)) 패턴은 스트림이 다 떨어질 때까지 반복합니다. getline이 스트림을 반환하고, 남은 것이 없으면 스트림이 false로 평가되기 때문입니다. 세 번째 인자를 빼면 대신 >>로 공백으로 구분된 토큰을 기준으로 나눕니다. 데이터가 사용하는 구분자를 골라 쓰세요.

메모리에서 문자열 만들기

반대 방향으로, ostringstreamsprintf의 버퍼 오버플로 위험이나 깨지기 쉬운 수동 연결 없이 서식 있는 문자열을 조립합니다. 로그 줄, 파일 이름, 숫자와 텍스트로 이어 붙인 메시지에 안성맞춤입니다.

cout과 함께 쓰던 <iomanip>의 모든 서식 - setw, setfill, setprecision, hex - 은 문자열 스트림에서도 똑같이 동작합니다. 그래서 채워 넣은, 고정 폭의, 또는 16진수 출력을 곧바로 string에 담아 얻을 수 있습니다.

함정: 재사용하기 전에 초기화하라

모두를 무는 함정은 초기화하지 않고 스트림을 재사용하는 것입니다. 스트림이 데이터의 끝에 도달하면 eoffail 플래그가 걸려 버리고, 이후의 모든 >>는 조용히 아무 일도 하지 않습니다. 오류도 없이 그저 낡은 값만 남습니다.

stringstream ss("10");
int a, b;
ss >> a;            // a = 10, 스트림이 이제 끝에 -> eof 플래그가 설정됨
ss.str("20");       // 새 텍스트를 불러온다...
ss >> b;            // 조용히 실패한다 - 오류 플래그가 여전히 설정되어 있어 b는 변하지 않음

오류 상태 내용을 모두 지워야 합니다. 올바른 순서는 먼저 clear(), 그다음 str()입니다.

관련된 두 가지 실수: ss.clear()는 플래그를 초기화하지만 버퍼를 비우지는 않습니다(그건 ss.str("")를 쓰세요). 그리고 <<로 계속 덧붙이는 stringstream은 계속 커지므로, 매 반복마다 더러워진 것을 재사용하지 말고 루프 안에서 새것을 만드세요.

다음: 배열

문자열 스트림은 텍스트를 타입이 있는 값으로, 또 그 반대로 바꾸는 깔끔한 방법을 주었습니다. CSV를 나누면서 채운 vector처럼 흔히 그 값들의 목록 전체를 다루죠. 이 값들의 고정된 컬렉션을 저장하고 인덱싱하려면 언어에서 가장 기본적인 컨테이너가 필요합니다. 다음으로 배열을 다룹니다. 선언하는 법, 인덱싱하는 법, 반복하는 법, 그리고 C++에서 그토록 많은 정의되지 않은 동작을 일으키는 범위 밖 접근을 피하는 법입니다.

자주 묻는 질문

C++에서 stringstream이란 무엇인가요?

std::stringstream은 키보드나 파일 대신 string을 기반으로 하는 스트림입니다. coutcin처럼 <<로 쓰고 >>로 읽으며, 그래서 텍스트를 파싱하고 메모리에서 문자열을 만드는 표준 도구가 됩니다. <sstream> 헤더에 들어 있습니다.

C++에서 stringstream으로 문자열을 int로 어떻게 변환하나요?

텍스트를 스트림에 넣고 숫자로 추출합니다: stringstream ss("42"); int n; ss >> n;. 변환이 실제로 성공했는지 확인하려면 if (ss)(또는 if (ss >> n))로 검사하세요. 간단한 경우에는 std::stoi가 더 짧지만, 하나의 문자열에 여러 종류의 값이 섞여 있을 때 stringstream이 빛을 발합니다.

stringstream에서 왜 clear()를 호출해야 하나요?

스트림이 데이터의 끝에 도달하면 eof/fail 비트가 설정된 채로 남아, 이후의 모든 >>는 조용히 아무 일도 하지 않습니다. 같은 stringstream을 새 내용으로 재사용하려면 ss.clear()로 이 오류 플래그를 초기화한 다음 ss.str(newText)로 새 데이터를 불러오세요. 그러지 않으면 읽기가 조용히 실패합니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기