[C언어, C++언어, Java언어 기초 프로그래밍 목차 링크 모음]
오늘은 문자열을 특정 구분자를 기준으로 나눠주는 strtok 함수를 살펴볼게요
string.h 라이브러리 파헤치기 strtok, strtok_s편
strtok()
문자열을 토큰으로 분리해주는 함수는 strtok입니다! 문법은 이와 같아요. 첫 번째 매개변수로 탐색할 문자열을 넣어주시고 두 번째 매개변수로 구분자를 넣어주시면 됩니다. 띄어쓰기를 기준으로 문자열을 분리하고 싶다면 " "를 넣어주고, 컴마를 기준으로 문자열을 구분하고 싶다면 ","을 넣어주면 되겠죠?
char * strtok(char str[], const char *delims);
리턴 값은 토큰이예요. 토근이 뭐냐.. 첫 구분자를 만나서 기존 문자열에서 그 구분자 만나기 전까지 문자열을 똑 떼어냅니다.
그게 token이예요. Hello world!를 " "를 기준으로 구분한다 하면 첫 토큰은 Hello가 되겠죠?!
strtok를 반복적으로 사용하면 구분자로 구분된 문자열들을 모두 알 수 있어요!
바로 예시를 볼까요?
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "my name is Ella";
char* token = strtok(str, " ");
while (token != NULL)
{
printf("%s\n", token);
token = strtok(NULL, " ");
}
return 0;
}
-----------------------------------------------------------------------------------------------------------------------
잠깐! 코드를 돌렸는데 비주얼 스튜디오에서 돌리면 다음과 같은 에러가 뜰 수 있어요
'strtok': This function or variable may be unsafe. Consider using strtok_s instead.
strtok는 안전하지 않으므로 strtok_s를 대신 사용하세요.
To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
이렇게 경고뜨는 거 없애고 싶으면 _CRT_SECURE_NO_WARNINGS 사용하랍니다.
이와 같은 에러를 보신다면 앞에
1
|
#pragma warning (disable: 4996)
|
cs |
이 코드를 넣어주시면 경고가 뜨지 않습니다.
gcc컴파일러를 사용하시는 경우에는 에러가 뜨지 않습니다. 밑에서 strtok_s사용방법도 알아볼게요
-----------------------------------------------------------------------------------------------------------------------
이어서~~~ 예시 코드를 보면 분명 첫 매개변수는 문자열이였는데 (7라인 코드 참조)
두 번째 strtok를 사용할 때에는 다음 토큰을 얻기 위해서 왜 NULL을 넣었지 의문이 들 수 있어요 (11라인)
그 해답은 내부 코드에 있습니다.
내부 코드를 잠깐 살펴볼게요
/* Copyright (c) Microsoft Corporation. All rights reserved. */
#include <string.h>
/* ISO/IEC 9899 7.11.5.8 strtok. DEPRECATED.
* Split string into tokens, and return one at a time while retaining state
* internally.
*
* WARNING: Only one set of state is held and this means that the
* WARNING: function is not thread-safe nor safe for multiple uses within
* WARNING: one thread.
*
* NOTE: No library may call this function.
*/
char * __cdecl strtok(char *s1, const char *delimit)
{
/* 스태틱으로 내부에 데이터를 항상 가지고 있고 이를 공유하고 있어요. */
static char *lastToken = NULL; /* UNSAFE SHARED STATE! */
char *tmp;
/*만약 첫 번째 아규먼트가 NULL이면 문자열을 19번째 라인에 저장된 lastToken으로 생각합니다. */
/*즉 두 번째 호출에서 첫 번째 아규먼트가 NULL이라고 해서 탐색할 문자열이 NULL이 되는 것이 아니다.*/
if ( s1 == NULL ) {
s1 = lastToken;
/*근데 저장되어 있는 문자열조차 NULL일 경우에 널을 리턴하고 종료됩니다. */
/*(41라인에 의해 찾는 문자열이 더 이상 없을 경우가 됨).*/
if (s1 == NULL)
return NULL;
} else {
/* strspn은 delimit가 아닌 문자가 시작되는 위치를 리턴하기 때문에 delimit가 공백일 경우 공백이 앞에 와도 무시됩니다.*/
s1 += strspn(s1, delimit);
}
/* 34번 라인: 문자열에서 delimit을 찾은 후 주소값을 리턴 (strpbrk는 찾은 문자열 주소 값을 리턴)*/
tmp = strpbrk(s1, delimit);
if (tmp) {
/* 만약 또 다른 delimit을 찾았으면, 문자열을 분리하고 상태를 저장 */
/* 두번째 구분자 주소가 0이 되버리므로 s1이 자동으로 두번째 구분자전까지 잘라집니다.*/
*tmp = '\0';
lastToken = tmp + 1;
} else {
/* 문자열 못찾으면 lastToken에 NULL을 저장해요. */
/* 즉 찾고자 하는 문자열이 없을 경우에만 내부의 데이터가 NULL이 됩니다. */
lastToken = NULL;
}
return s1;
}
나름 자세히 주석을 달려고 했는데 이해하셨나요?
간단하게 규칙을 아래와 같이 이해하면 됩니다. ↓
만약 같은 문자열에서 탐색을 계속 이어나가고 싶다면 두 번째 strtok 호출 시 첫 번째 아규먼트로 널 포인터를 전달해준다.
이유는 첫 아규먼트가 널이면 현재 저장되어 있는 남은 문자열을 데이터로 사용하기 때문!
만약 널이 아니면 새로운 검색이 시작된다 생각해 내부 데이터를 초기화하기 때문이다.
여기까지 문자열을 구분하는 strtok원리와 사용 방법에 대해서 알아봤어요.
strtok_s()
strtok_s에는 strtok에 없던 context라는 새로운 매개변수가 들어가는데요 context는 분리된 후 남은 문자열이 들어갑니다.
이 차이 빼면 strtok랑 똑같아요 ㅎㅎ
#include <stdio.h>
#include <string.h>
int main()
{
char str[1000] = " hello my name is Ella";
char * token = NULL;
char * context = NULL;
token = strtok_s(str, " ", &context);
while (token != NULL)
{
printf("토큰 문자열 :%s, 남은 문자열:%s\n", token, context);
token = strtok_s(NULL, " ", &context);
}
return 0;
}
결과를 확인하면 아래와 같습니다.
쉽죠잉~!?
참고로 이와 비슷한 함수로 자바에서 StringTokenizer() 메소드가 있습니다. ㅎㅎ
오늘은 간단하게 strtok 함수에서 NULL포인터를 전달하는 이유를 파헤쳐보았어요.
다음에 또 봐요
최신 댓글