C언어/string.h

[C언어] strtok 함수 (문자열 자르기)

아무일도없었다 2022. 9. 20. 23:28

사용범위

Windows, Unix 등 모든 OS에서 사용가능한 표준 API 함수

기능

C언어 표준 함수로 문자열을 특정 문자열 기준으로 자를때 사용한다.

헤더

#include <string.h>

※ strtok 함수 사용시 string.h 파일을 include 하지 않는다면 컴파일시 error 발생 ※


함수

char* strtok(char* str, const char* delim);

파라미터

  • char* str
    • 자르려는 문자열 버퍼를 입력한다.
    • 처음 문자열을 자른(= strtok 호출) 이후 남은 문자열을 다시 자를 경우 NULL 을 입력한다.
  • const char* delim
    • str 문자열을 잘라낼 기준 문자열을 입력한다.

반환값 (return)

delim 을 기준으로 문자열을 잘라낸 경우 : 잘라낸 문자열(포인터)을 반환한다.
delim 을 찾지 못하거나 더이상 없는 경우 : NULL 을 반환한다.

잡학지식

strtok 은 사용시 굉장히 많은 주의가 필요하며 아래의 이유로 사용자체를 권장하지 않는다.

 

첫번째로 strtok 함수원본 데이터 문자열(str)을 훼손한다.

원본 string 데이터

위와 같은 string 데이터가 있다고 가정하고 strtok 을 사용하여 공백문자 ' '  를 기준으로 문자열을 자르면 "hello", "strtok", "func", "!" 문자열 4개로 잘리는것을 확인할 수 있다.

 

하지만 원본 string 데이터를 확인해보면 아래와 같이 변경된것을 확인할 수 있을것이다.

원본 string 데이터 였던 것

따라서 원본 데이터의 무결성을 보장해야하는 경우에는 절대로 사용해서는 안된다.

 


 

두번째로 strtok 함수스레드 안전(thread safety) 하지 않은 함수이다.

 

token = strtok(str, delim); // first
   ...
token = strtok(NULL, delim); // other
   ...

 

strtok 은 처음 사용시 그대로 사용하지만, 이후에 남아있는 문자열을 다시 자를경우에 str 이 아닌 NULL 을 입력하게 되어있다.

 

이는 strtok 함수 내부에서 static pointer 변수를 통해 token 이후 남아있는 문자열의 포인터를 저장하고 있기 때문에 (token + 1) 과 같은 포인터 연산을 할 필요가 없기 때문이다.

 

하지만 이후 멀티스레드 환경이 등장하면서 각각의 스레드가 동시에 strtok 함수를 호출할 경우 내부의 static pointer 변수가 덮어씌워지는 치명적인 오류가 발생했다. (스레드 안전(thread safety) 하지 않다.)

 

이러한 문제를 해결하고자 strtok 과 같은 기능을 하면서 thread safety 한 새로운 API가 개발되었다.

 

하지만 안타깝게도 Windows 와 Unix 의 함수명이 다르다. (크로스 플랫폼 개발시 주의 !)

 

Windows 의 경우 strtok 함수 대신 strtok_s 함수 사용을 권장하고 있으며,

Unix(Linux,Aix,...) 의 경우 strok 함수 대신 strtok_r 함수 사용을 권장하고 있다.


 

<소스 코드>

#include <stdio.h>

int main() {
    char buffer[] = "HelloABCstrtokABCfunctionABC!!";
    char *ret_token = NULL;

    // ABC 문자열을 기준으로 잘라낸다.
    ret_token = strtok(buffer, "ABC");

    while(ret_token != NULL) {
        printf("token[%s]\n", ret_token);
        ret_token = strtok(NULL, "ABC");
    }

    // 원본 훼손 (주의)
    printf("buffer[%s]\n", buffer);
    
    return 0;
}

 

※ 실행 결과

token[Hello]
token[strtok]
token[function]
token[!!]
buffer[Hello]

 

반응형