알고리즘을 백준으로만 공부하면서 풀다가 최근에야 프로그래머스로 몇개의 코딩테스트를 응시하였었다.
응시 후 드는 생각은 '풀이 방식은 알 것 같은데 구현시간이 너무 느리지...' 였다. 한 번 그러고 말면 괜찮은데 여러번 반복이 되니까 나의 구현 실력에 큰 문제가 있음을 깨닫고 쉬운문제더라도 빠르게 해결하기 위해서 노력중이다.
이번에는 느린 구현속도에 영향을 크게주는 c++로 문자열 처리하기에 대해서 정리하려고 한다.
사실 문자열 처리하는 문제는 파이썬을 이용하면 아주 편하기는 하다. 하지만 지금까지 c++로 공부하였기도 하고, 실력자 분들은 c++로도 잘만 푸시니 c++문자열 처리를 따로 공부하여 정리하려고 한다.
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
vector<string> split(string str, char delimiter);
int main(){
string test = "this is test";
vector<string> result = split(test, ' ');
for (int i=0;i<result.size();i++){
cout << result[i] << " ";
}
}
vector<string> split(string input, char delimiter) {
vector<string> answer;
stringstream ss(input);
string temp;
while (getline(ss, temp, delimiter)) {
answer.push_back(temp);
}
return answer;
}
stringstream 은 마치 문자열을 입력 스트림으로 생각하게 해주는 역할이다.
istream & getline(istream& ls, string& str, delim)
getline 함수는 입력 스트림에서 문자들을 읽어서, 인자로 받은 문자열에 저장한다.
즉, split을 할 문자열을 stringstream으로 만들고 getline의 delimiter를 이용하여 원하는 구분자로 split을 가능하게 해준다.
#include<iostream>
#include<string>
#include<vector>
#include<sstream>
using namespace std;
int main()
{
string str="java,c,c++,python";
int previous =0;
int current=0;
vector<string> x;
x.clear();
current= str.find(',');
//find는 찾을게 없으면 npos를 리턴함
while(current!=string::npos){
string substring=str.substr(previous,current-previous);
x.push_back(substring);
previous = current+1;
current=str.find(',',previous);
}
x.push_back(str.substr(previous,current-previous));
for(int i=0;i<x.size();i++){
cout<<x[i]<<endl;
}
return 0;
}
혹은 더 간단하게
#include<iostream>
#include<string>
#include<vector>
#include<sstream>
using namespace std;
int main()
{
string str="java,c,c++,python";
int previous =0;
int current=0;
vector<string> x;
x.clear();
//find는 찾을게 없으면 npos를 리턴함
while((current = str.find(',', previous)) != string::npos){
string substring=str.substr(previous,current-previous);
x.push_back(substring);
previous = current+1;
}
x.push_back(str.substr(previous,current-previous));
for(int i=0;i<x.size();i++){
cout<<x[i]<<endl;
}
return 0;
}
substr은 문자열의 부분 문자열을 리턴하는 string의 메소드로서
basic_string substr(size_type pos = 0, size_type count = npos) const;
다음과 같다. pos는 첫번째 문자의 위치이고, count 길이만큼의 문자열을 리턴한다.
size_type find(const basic_string& str, size_type pos = 0) const;
문자열에서 str 의 위치를 리턴한다.
str은 찾고자하는 문자열이고 pos는 검색을 시작할 위치이다.
find와 substr을 이용하는 장점은 delimeter가 두종류 이상이더라도 다음과 같이 처리가 가능하다.
#include<iostream>
#include<string>
#include<vector>
#include<sstream>
#include<algorithm>
using namespace std;
int main()
{
string str="java/c,c++,python";
int previous =0;
int current=0;
vector<string> x;
x.clear();
int current1 =str.find(",",previous);
int current2 =str.find("/",previous);
if(current1 == string::npos){
current = current2;
} else if(current2 == string::npos) {
current = current1;
} else{
current = min(current1,current2);
}
//find는 찾을게 없으면 npos를 리턴함
while(current!=string::npos){
string substring=str.substr(previous,current-previous);
x.push_back(substring);
previous = current+1;
int current1 =str.find(",",previous);
int current2 =str.find("/",previous);
if(current1 == string::npos){
current = current2;
} else if(current2 == string::npos) {
current = current1;
} else{
current = min(current1,current2);
}
}
x.push_back(str.substr(previous,current-previous));
for(int i=0;i<x.size();i++){
cout<<x[i]<<endl;
}
return 0;
}
여기서는 두종류라 그냥 조건문으로 처리하였지만, 세개 이상일 경우 반복문을 이용하여 npos를 제외한 current를 최소값을 찾는 함수를 만들어도 될 것 같다.
추가:
아무래도 조건문이 영 찝찝해서 구분자 여러개를 배열에 담아 find하는 함수를 작성해보았다.
#include<iostream>
#include<string>
#include<vector>
#include<sstream>
#include<algorithm>
using namespace std;
// 구분자 배열, split할 문자열, 배열크기, previous
int findMany(string delArr[], string targetStr, int numdel, int previous){
int *curs = new int[numdel];
int min = 987654321;
for(int i=0; i<numdel; i++){
curs[i] = targetStr.find(delArr[i], previous);
if(curs[i] == string::npos){
curs[i] = 987654321;
}
if(curs[i] < min){
min = curs[i];
}
}
if(min == 987654321){
return string::npos;
}
return min;
}
int main()
{
string str="java/c,c++,python";
int previous =0;
int current=0;
vector<string> x;
x.clear();
string findchs[2] = {",", "/"};
//find는 찾을게 없으면 npos를 리턴함
while((current = findMany(findchs, str, 2, previous))!=string::npos){
string substring=str.substr(previous,current-previous);
x.push_back(substring);
previous = current+1;
}
x.push_back(str.substr(previous,current-previous));
for(int i=0;i<x.size();i++){
cout<<x[i]<<endl;
}
return 0;
}
참고:
https://ssungkang.tistory.com/entry/C-string-문자열-나누는-split
https://modoocode.com/236
https://chbuljumeok1997.tistory.com/42