C++로 문자열 split하기

greeeedy·2022년 5월 15일
0

서론

코딩테스트를 합격하기 위해 여러 방법으로 공부를 하고 있다. 가장 빠른 학습 방법은 잘하는 분들의 코드와 설명을 보는 것인데, 그 과정에서 알게된 팁 두 가지를 공유하고자 한다.

bits/stdc++.h 헤더

gcc 계열의 컴파일러에는 미리 내장되어 있다. 우리가 ps를 하기 위해 사용할 법한 모든 헤더파일들을 include 해놓은 헤더파일이다. 실제로 필자는 S기업의 오프라인 코딩테스트 시험에서 memset 함수를 사용해야 했는데 헤더명이 기억나지 않았고, 이 헤더파일을 포함함으로써 위기(?)를 모면했던 경험이 있다.

내용은 아래와 같다.

// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdalign>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif

// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

...

#endif

Windows에서 Visual Studio를 통해 사용하는 경우 이것이 없으므로, 사용하기 위해서는 따로 만들어 추가해주어야 한다.

split 사용하기

글쓴이는 문자열을 다루는 경우, 특히 split 기능이 필요한 경우에 C++ 대신에 JS를 사용하곤 했다. 문자열을 다룰 때 편한 JS임에도 우선순위 큐와 같은 자료구조의 부재는 무언가 아쉬움을 남게 했다. 그렇게 파이썬으로의 전향을 고심하던 중 C++에도 문자열을 편히 다룰 수 있는 stringstream 클래스(?)를 알게 되어 이를 공유하고자 한다.

헤더

상기했던 bits/stdc++.h를 포함해도 되고, 그것이 싫다면 sstream 헤더를 추가하면 된다.

사용법

생성자

string str = "2022-05-15"
stringstream st(str);

str 변수는 기존 string 클래스의 인스턴스이고 우리가 다루고 싶은 문자열이다. 이를 stringstream의 생성자의 인자로 전달해 st 변수를 생성한다.

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    string str = "2022-05-15";
    string tmp;
    stringstream st(str);

    while (getline(st, tmp, '-'))
        cout << tmp << endl;

    return 0;
}

getline 함수의 두번째 매개변수(tmp)는 인자를 참조형으로 받아 수정한다. 첫번째 매개변수(st)를 세번째 매개변수('-')를 토큰으로 삼아 스플릿한 결과물을 tmp에 저장하는 것이다.

출력 결과
2022
05
15

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    string str = "2022---05-15"; // 토큰이 연속해서 나오는 경우
    string tmp;
    stringstream st(str);

    while (getline(st, tmp, '-'))
        cout << tmp << endl;

    return 0;
}

토큰으로 사용되는 문자가 여러번 등장해서 나오는 경우 JS의 split과 마찬가지로 "" 문자열이 담기는 경우가 있게 된다.

tmp 값의 변화
"2022" -> "" -> "" -> "05" -> "15"

이를 방지하기 위해서 토큰화할 문자를 공백으로 치환한 후 다루는 방법을 택했다.

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    string str = "2022   05 15";
    string tmp;
    stringstream st(str);

    while (st >> tmp)
        cout << tmp << endl;

    return 0;
}

stringstream 클래스의 >> 연산자가 오버라이딩 되어있는 것으로 추측되고, 이는 공백 문자를 토큰으로 사용하여 split한 결과를 우항의 변수에 담아준다.

출력 결과
2022
05
15

결론

">>" 를 사용한 스플릿의 경우 공백이 연속해서 나와도 빈 문자열을 담는 경우가 없게 된다. 실사용에선 토큰화할 문자를 공백으로 치환하는 과정을 거친 후, ">>" 연산자를 이용한 split을 사용하는 것이 좋아보인다. 또한 tmpstring이 아닌 int로 선언하고 위 과정들을 시행하면 자동으로 casting 과정까지 이루어진다. 이로써 C++로 문자열을 조금 더 편하게 다룰 수 있게 되었다.

0개의 댓글