728x90

c++에서 문자열을 다루는 일은 매우 짜증나는 일이다.

string클래스는 매우 좋은 클래스이나 치명적인 문제점이 있는데 c style의 문자열(cstring)과 중복되는 부분을 손을 안 본 곳이 많다는 점이다.

그래서 경우에 따라서는 이 둘을 넘나들면서 사용해야하는 경우가 생긴다는 것이다.


그 중에 가장 대표적인 예시가 문자열 자르기이다. 다른 말론 토크나이징이라고 한다.

string클래스에서는 자르는 함수가 아예 없다... 그래서 정석적인 방법은 두가지가 있다.


하나는 boost를 추가하는 것이다. 이게 뭐 정석은 아닐수도 있다. 근데 cpp 주력으로 쓰는 사람치곤 부스트를 안쓰는 사람은 없다.

문제는 알고리즘 대회같은 대서는 부스트를 쓸수 없다... 하지만 토크나이징은 중요하므로 하는 방법을 알아두어야한다.


두번째는 cstring헤더(string.h와 동일)의 strtok함수를 사용하는 것이다. 사실 알고리즘 쓰는 사람들은 울며 겨자먹기로 이것만 쓴다.

혹은 부스트가 무겁다고 생각해서 안쓰거나... 하는 사람 아직 본적없다.


필자는 일단은 두번째 방법만 논하려고한다. 첫번째 방법은 언젠가 포스팅 하는 날이 있겠지만 부스트를 다루면서 일괄 포스팅하려하므로 아직은 아니다.


단계별로 예시를 보도록 하자.


string str_arr[1000];
int str_cnt;

일단 필자의 목표는 문자열을 잘라서 배열에 넣는게 목표이다.

배열대신 vector를 써도 되지만 stl에 익숙치 않은 여러분들을 위해서 배열로 하는 예제를 보여드리겠다.


string a = "My name is kukaro";

자를 대상의 문자열이다. space를 기준으로 잘라보자.


char str_buff[1000];
strcpy(str_buff, a.c_str());

일단 string클래스 자체는 strtok을 사용할 수 없으므로 c style string으로 바꿔주어야한다.

그러므로 strcpy함수를 사용해서 복사해 준다. strtok은 오로지 char 배열만 되므로(포인터 안됨) 위처럼 사용해주어야한다.

s.c_str()의 반환결과는 포인터이기 때문에 결국 실체가 있는 배열에 담는 작업을 해줘야한다.

위의 작업이끝나면 str_buff에는 문자열 a의 c style string값이 들어가게된다.


아 참고로 혹시나 햇갈리는 사람을 위해서 말해주자면 여기서 말하는 포인터는 문자열 리터럴을 말하는 것이다.

예를 들면 포인터에 동적할당으로 값 넣는건 사용할 수 있다.


char *str_buff = (char *) malloc(sizeof(char) * 1000);

예를 들면 이렇게 하면 사용할 수 있다는 것이다.

이렇게 쓰려면 cstdlib헤더를 추가해줘야하지만.

하지만 우리가 쓰고 있는 언어는 c++이므로 위의 코드는 아래처럼 나타낼 수 있다.


char *str_buff = new char[1000];

이런식으로 동적할당해주면 되므로 포인터가 안된다는 말은 동적할당하지 않고 리터럴인게 안된다는 뜻이다..

못 알아 듣겠으면 걍 포인터 안된다고 생각하라.



하... 벌써부터 짜증난다. 아직 더 남았다.


char *tok = strtok(str_buff, " ");

tok은 포인터로 선언해줘야한다. 어짜피 strtok이 포인터를 반환하기 때문이다.

그리고 좌변에는 내가 자를 c style 문자열 배열을(포인터 안된다고 했다.) 우측에는 자를 토큰을 넣는다(여기는 포인터 된다.)

이렇게 짜르면 tok의 경우 맨 첫주소를 가지고 있다.

여기서 tok을 출력한다면 My가 출력될 것이다.


while (tok != nullptr) {
str_arr[str_cnt++] = string(tok);
tok = strtok(nullptr, " ");
}

strtok은 일치하는게 없으면 NULL을 반환한다. cpp에서는 nullptr을 써주는게 정석인데 뭐 NULL을 써도 상관은 없으니 알아서하라.

문자열 배열에 담는 장면인데 지금 c style string을 다시 cpp style string으로 변환해줘야하므로 토큰을 string클래스 생성자로 다시 넣어준다.


tok = strtok(nullptr, " ");

while문 내부에 첫번째 파라메터로 NULL을 주게되면 내부 버퍼에 저장되어있는 포인터에서 다음 " "를 찾고 그 뒤에 문자열의 주소를 반환하게 된다.

이렇게 해서 계속해서 순회하면 모든 문자열을 잘라낼 수 있다.


코드의 전문은 아래와 같다.


#include <iostream>
#include <cstring>

using namespace std;
string str_arr[1000];
int str_cnt;

int main() {
string a = "My name is kukaro";
char *str_buff = new char[1000];
strcpy(str_buff, a.c_str());

char *tok = strtok(str_buff, " ");
while (tok != nullptr) {
str_arr[str_cnt++] = string(tok);
tok = strtok(nullptr, " ");
}

for (int n = 0; n < str_cnt ; n++) {
cout << str_arr[n] << endl;
}
return 0;
}

위의 코드로 모든 문자열을 " "로 잘라내는걸 확인할 수 있다.

로직을 다시 정리하자면


1.string을 char[]로 변환

2.char[]를 strtok으로 잘라냄

3.잘라낸 char[]를 다시 string으로 변환

4.string배열에 적재

+ Recent posts