0. 들어가기 전에
- 카카오 코테를 풀다 간단한 문자열 파싱 문제에 과도하게 시간 투자……..
- 파이썬을
split()
을 사용하면 되지만 C++이 주 언어이기도 하고 속도가 빨라 포기할 수 없기에 C++에서도 비슷한 메서드가 없을까? 찾아봄..
이번 글에서는 C++로 간편하게 문자열을 파싱하고 조립하는 방법에 대해 다루어 보겠습니다.
1. 문자열 파싱이란?
특정 조건을 기준으로 문자열을 분리하는 방법
다음의 두 가지 문자열이 있다고 가정해 봅시다.
date = “2025.07.29”
friends = “muzi prodo pitch”
우리의 목표는 이 문자열에서 공백이나 .을 제외하고 특정 데이터만 추출하는 것입니다. 아래에서 다양한 방법을 이용하여 문자열을 파싱해 봅시다.
2. 파이썬에서 문자열 파싱
파이썬에서는 다음과 같이 간단히 split함수로 문자열을 분리할 수 있습니다.
str = "prodo muzi pitch"
list = str.split()
3. C++에서 문자열 파싱 방법
1) 첫 번째: substr 사용하기
- substr(a, b)는 a로부터 b만큼의 문자열을 반환한다.
- 문자열의 크기를 주의해야 하며 outofbound의 오류를 주의해야 한다.
물론 이 방식으로 하드코딩해서 문자열을 파싱할 수 있지만 복잡한 구현 문제에서는 문자열 크기, 인덱스 등을 신경써야 해서 매우 불편합니다.
예를 들어 “2025.07.25” 에서 year, month, day를 분리한다고 할 때, 다음의 로직을 거쳐야 합니다.
.
구분자 인덱스 찾기- 이미 .의 위치를 알고 있다면 인덱스를 첫 번째 인자로 인자로 넣기
- .의 위치를 직접 찾는다면
find(str.begin(), str.end(), ‘.’);
substr(index, size);
2) 두 번째: istringstream
istringstream이란?
istringstream(iss) 문자열(string)을 마치 파일이나 표준 입력처럼 한 번에 쪼개서 읽고 싶을 때 쓰는 스트림 클래스
동작 원리
오버로딩된 입출력 연산자(<<,
>>
)를 내부적으로 활용하는 방식입니다. <sstream>을 이해하기 위해 STL의 내부적인 operator 함수의 오버로딩된 형태를 간단히 살펴보겠습니다.
간단히 활용하는 방식만 보고싶은 분들은 아래의 특징으로 넘어가면 됩니다.
- STL에서
operator>>
오버로딩 구현 형태
template<typename _CharT, typename _Traits>
basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __in, int& __n)
{
// sentry는 공백을 자동으로 건너뛰고 스트림 상태를 검사하는 도우미
typename basic_istream<_CharT, _Traits>::sentry __cerb(__in, true);
if (__cerb) {
// 오류 상태 추적용 변수
ios_base::iostate __err = ios_base::goodbit;
// num_get 사용: 문자열로부터 숫자를 읽어 int로 변환
typedef num_get<_CharT, istreambuf_iterator<_CharT, _Traits>> __num_get;
const __num_get& __ng = use_facet<__num_get>(__in.getloc());
// 읽기 실행
__ng.get(
istreambuf_iterator<_CharT, _Traits>(__in),
istreambuf_iterator<_CharT, _Traits>(),
__in, __err, __n
);
// 오류 상태 업데이트
if (__err != ios_base::goodbit)
__in.setstate(__err);
}
return __in;
}
__in.flags()
:std::hex
,std::dec
,std::skipws
등의 입력 포맷 상태를 가져옴use_facet<>()
: 로케일(locale)에 따라 숫자 해석 방식 다르게 적용 가능num_get<>::get(...)
: 문자들을 읽어 원하는 타입(int, double 등)으로 변환istreambuf_iterator
: 스트림에서 문자를 차례로 읽는 반복자 역할- 실패하면
failbit
플래그를 설정하여 오류 감지 가능
- isstringstream 동작 원리
istringstream iss("123");
int x;
iss >> x;
sentry
가 공백 등을 처리하고 스트림 유효성 확인istreambuf_iterator
를 사용해 스트림 버퍼에서 문자 읽음num_get<>::get()
이123
을 숫자로 해석- 변환 결과가
x
에 저장됨 - 실패 시
failbit
등의 플래그 설정됨
특징
- istringstream(iss)에서 기본 입력 연산자(>>)를 쓰면 자동으로 공백 기준 으로 문자열이 잘려서 파싱
- 콤마(,)나 슬래시(/) 같은 특정 구분자로 파싱하고 싶을 때는 getline을 사용
- int나 char으로 가져올 경우 원하는 자료형으로 가져올 수 있다.
첫 번째 성질을 이용하여 while (iss >> str) 으로 반복 가능합니다.
istringstream iss(friends);
while (iss >> name) {
// ...
}
iss >> name
은istream
에서 공백(space
,tab
,newline
)을 기준으로 문자열을 읽음- 더 이상 읽을 게 없거나 오류가 발생하면
while
루프를 자동 종료
이 반복 형태를 이용하여 아래에서 다양한 사용 예시에 대해 다뤄봅시다.
사용 예시
1. 공백으로 파싱한 값 가져오기
#include<iostream>
#include<sstream>
#include<vector>
using namespace std;
int main(void) {
string friends = "muzi prodo pitch";
vector<string> result;
istringstream iss(friends);
string name;
while (iss >> name) result.push_back(name);
for (string str : result) cout << str << " ";
return 0;
}
출력: muzi prodo pitch
2. 특정 구분자로 파싱한 값 가져오기
- getline과 함께 사용하기(특정 구분자 기준)
#include<iostream>
#include<sstream>
#include<vector>
using namespace std;
int main(void) {
string date = "2025.07.29";
vector<string> result;
istringstream iss(date);
string num;
while (getline(iss, num, '.')) result.push_back(num);
for (string str : result) cout << str << " ";
return 0;
}
- 특정 구분자를 공백으로 변환해서 사용
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
int main(void) {
string date = "2025.07.29";
vector<string> result;
replace(date.begin(), date.end(), '.', ' ');
istringstream iss(date);
string num;
while (iss >> num) result.push_back(num);
for (string num : result) cout << num << " ";
return 0;
}
getline(iss, str, 구분자)으로 사용해도 되지만 공백으로 변환해서 사용하는 것이 편합니다.
특정 구분자(`.`, `/`, …)를 공백으로 변환하면 기본 istringstream으로 사용 가능하죠.
- 원하는 자료형으로 가져오기
int로 가져올 경우 숫자 단위로 가져옵니다.
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
int main(void) {
string date = "2025.07.29";
vector<int> result;
replace(date.begin(), date.end(), '.', ' ');
istringstream iss(date);
int num;
while (iss >> num) result.push_back(num);
for (int num : result) cout << num << " ";
return 0;
}
출력: 2025 7 29
char로 가져올 경우 한 문자씩 가져옵니다.
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
int main(void) {
string date = "2025.07.29";
vector<char> result;
replace(date.begin(), date.end(), '.', ' ');
istringstream iss(date);
char num;
while (iss >> num) result.push_back(num);
for (char num : result) cout << num << " ";
return 0;
}
출력: 2 0 2 5 7 2 9
4. 정리
- isringstream과 마찬가지로 osstream으로도 출력 형태를 지정할 수 있습니다.
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
ostringstream oss;
for (size_t i = 0; i < numbers.size(); ++i) {
oss << numbers[i];
if (i != numbers.size() - 1) {
oss << ","; // 마지막 숫자 뒤엔 쉼표 안 붙임
}
}
string result = oss.str(); // 문자열로 변환
cout << result << endl; // 출력: 1,2,3,4,5
}
- sstream의 문자열 조립 라이브러리를 사용하면 파이썬의 split처럼 간단한 함수로 처리 가능!!!!
앞으로 복잡한 문자열 처리가 나와도 빠르게 파싱하고 넘어가도록 합시다
'STL(Standard Library)' 카테고리의 다른 글
최대 힙 최소 힙 (0) | 2023.12.31 |
---|---|
[C++] STL algorithm 조건 지정자 (0) | 2023.12.31 |
STL pair, tuple 사용법 (1) | 2023.12.26 |
[C C++] STL deque (0) | 2023.12.20 |
[C C++] STL에서 Duque와 Vector의 차이점 (0) | 2023.12.19 |
경이로운 BE 개발자가 되기 위한 프로그래밍 공부 기록장
도움이 되었다면 "❤️" 또는 "👍🏻" 해주세요! 문의는 아래 이메일로 보내주세요.