코딩테스트를 합격하기 위해 여러 방법으로 공부를 하고 있다. 가장 빠른 학습 방법은 잘하는 분들의 코드와 설명을 보는 것인데, 그 과정에서 알게된 팁 두 가지를 공유하고자 한다.
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 기능이 필요한 경우에 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을 사용하는 것이 좋아보인다. 또한 tmp를 string이 아닌 int로 선언하고 위 과정들을 시행하면 자동으로 casting 과정까지 이루어진다. 이로써 C++로 문자열을 조금 더 편하게 다룰 수 있게 되었다.