strtok, strpbrk 구분자로 분리하기


strtok, strpbrk 구분자로 분리하기

이번에는 strtok, strpbrk에 대해서 알아보겠다.
이 함수들을 사용하려면 string.h 파일을 include 해야된다.

두개의 함수 원형을 보자.

1
2
char* strtok(char *strToken, const char *strDelimit);
char* strpbrk(const char *stringconst char *strCharSet);
cs


함수의 원형은 이렇다. strtok 사용법을 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
main()
{
    char str[100= "aossuper8.tistory.com";
    char *token, *context;
 
    token = strtok_s(str, "."&context);
    while (token) {
        puts(token);
        token = strtok_s(NULL"."&context);
    }
}
cs

< 출력 >


포인터 변수 token을 만들어서 strtok 반환 주소값을 받는다. 첫번째 인자값에는 구분할 문자열을 넣어주고
두번째 인자값에는 구분자를 넣어주면 된다.
9행~11행이 없으면 출력은 aossuper8 만 나올것이다.
그렇기 때문에 반복문을 돌려서 str이 '\0'을 만날때까지 돌려준다.

그럼 함수가 어떻게 동작하는지 구현을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
char* strtok(const char *stringconst char *strCharSet)
{
    static char *nextTokenPos;
    char *curTokenPos;
    char *curStrPos;
 
    if (string != NULL) {
        curTokenPos = string;
        curStrPos = string;
        nextTokenPos = (char*)-1// 초기화 해주는 용도
    }
    else {
        curTokenPos = nextTokenPos;
        curStrPos = nextTokenPos;
    }
 
    if (nextTokenPos == NULLreturn NULL;
    while (1) {
        if (*curStrPos == 0) {
            nextTokenPos = NULL;
            break;
        }
        if (*curStrPos == *strCharSet) {
            *curStrPos = 0;
            nextTokenPos = curStrPos + 1;
            break;
        }
        curStrPos++;
    }
    return curTokenPos;
}
main()
{
    char str[100= "aossuper8.tistory.com";
    char *pos;
    
    pos = strtok_s(str, ".");
    while (pos) {
        puts(pos);
        pos = strtok_s(NULL".");
    }
}
cs

출력은 똑같이 나온다.
4행에서 nextTokenPos를 static으로 메모리를 전역변수와 같이 활성화를 해준다. 그 이유는 41행에 보면 NULL을 넘겨주고 있다.
그렇기 때문에 static으로 메모리를 전역변수와 같이 활성화 해주고 있다.
8행에서 string가 NULL이 아니면 curTokenPos와 curStrPos는 string을 바라보게 설정하고 nextTokenPos는 초기화 해준다.
13행에서 NULL이 아니면 curTokenPos와 curStrPos는 nextTokenPos를 바라보게 한다.
18행에서 nextTokenPos가 NULL이면 NULL을 반환시킨다.
20행에서 curStrPos가 0이면 nextTokenPos를 NULL로 초기화 시키고 while문을 빠져나온다.
24행에서 curStrPos가 strCharSet 값과 같다면 curStrPos를 0으로 초기화 시키고
nextTokenPos를 curStrPos + 1(주소값을 +1 해줌으로써 구분자 다음으로 이동시킴)해줘서 nextTokenPos에 주소값을 넣는다.
그리고 반복문을 빠져나온다. 31행에서 curTokenPos 주소를 넘겨준다.

다시 말하자면 첫번째 인자가 문자열일 때 두번째 인자를 기준으로 문자열을 자른다.
인자가 문자열일 때 그 기준대로 문자열을 잘라 반환한다.
strtok(char *s, const char *t) 라고할 때 *s를 모든 t와 비교하고 s를 1증가 시킨다.
t와 일치하면 공백으로 치환을 한다. 잘린 글자가 저장된 주소를 리턴한다.

그 다음 실행 때 부터는 NULL문자를 첫 번째 인자로 넣어야만 첫 실행 때 나눴던 문자열의 다음 토큰을 반환한다.
즉, 반환된 번지는 static 변수에 저장이 되어있어야 NULL이 들어왔을 때 처리가 가능하다는 얘기이다.
NULL이면 기존에 저장한 주소부터 다시 t와 일치하는지 검사를 한다. t와 일치하면 다시 NULL로 치환하고 잘린 다음 주소를 반환.

NULL이 아니라 또 다시 다른 문자열을 첫 번째 인자로 전달 시 다시 문자열을 나눈다.
이정도 하면 코드는 이해갔을거다.



strpbrk 함수를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
main()
{
    char str[100= "aossuper8.tistory.com";
    char *pos;
 
    pos = strpbrk(str, ".");
    while (pos) {
        puts(pos++);
        pos = strpbrk(pos, ".");
    }
}
cs

< 출력 >


사용법은 strtok랑 같다. 근데 출력 되는것이 다르다. 왜 다를까? 함수 구현을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
char* strpbrk(const char *stringconst char *strCharSet)
{
    const char *pTempChar;
    while (*string) {
        pTempChar = strCharSet;
        while (*pTempChar) {
            if (*string == *pTempChar)
                return (char*)string;
            pTempChar++;
        }
        string++;
    }
    return NULL;
}
main()
{
    char str[100= "aossuper8.tistory.com";
    char *pos;
 
    pos = strpbrk(str, ".");
    while (pos) {
        puts(pos++);
        pos = strpbrk(pos, ".");
    }
}
cs

6행에서 string이 한칸 이동할때마다 strCharSet의 전체 문자열과 비교해야 하기 때문에 두번째 while 문으로 들어가기 전
pTempChar을 strCharSet로 초기화하고 들어간다.
8행에서 string이 pTempChar와 같다면 string을 반환한다.
맞지 않으면 pTempChar의 주소값을 증가시켜서 또 한번 비교를 한다.
pTempChar이 '\0'를 만나면 while문을 빠져나와 string 주소값을 증가시켜서 또 다시 비교를 하게 한다.

이정도 하면 코드가 이해 되겠다.

댓글

Designed by JB FACTORY