C/C++ 표준 라이브러리: `strtok` 함수를 이용한 문자열 토큰 분리

개요

C 및 C++ 표준 라이브러리에서 제공하는 strtok 함수는 널(null)로 끝나는 문자열을 지정된 구분자(delimiter)를 기준으로 여러 개의 '토큰(token)'으로 분리하는 데 사용됩니다. 이 함수는 주로 C 스타일 문자열(`char*`)을 다룰 때 유용하며, <cstring> (C++에서는 <string.h>) 헤더 파일에 정의되어 있습니다.

함수 시그니처

char* strtok( char* str, const char* delim );
  • str: 분리할 문자열을 가리키는 포인터입니다. 첫 번째 호출 시에는 원본 문자열을 전달하고, 이후 호출에서는 NULL을 전달합니다.
  • delim: 토큰을 구분하는 데 사용될 하나 이상의 문자를 포함하는 널 종료 문자열을 가리키는 포인터입니다.

동작 방식

strtok 함수는 호출될 때마다 다음 토큰을 찾아 반환하며, 내부적으로 정적(static) 변수를 사용하여 이전 호출의 상태를 기억합니다. 따라서 여러 번 호출하여 동일한 문자열에서 연속적인 토큰을 추출할 수 있습니다.

  1. 첫 번째 호출 (strNULL이 아닌 경우)

    • strtokstr에서 delim에 포함되지 않는 첫 번째 문자를 찾습니다. 이 문자가 토큰의 시작점이 됩니다.
    • 만약 이러한 문자를 찾을 수 없다면, str에 토큰이 없다는 의미이므로 NULL을 반환합니다.
    • 토큰 시작점을 찾으면, 그 지점부터 delim에 포함된 첫 번째 문자를 검색합니다.
    • 이 구분자를 찾으면, 해당 구분자 위치를 널 문자('\0')로 교체하여 현재 토큰을 종료시키고, 다음 토큰이 시작될 위치를 내부적으로 저장합니다.
    • 구분자를 찾지 못하면, str의 나머지 부분이 하나의 토큰이 되며, 이후 strtok 호출은 NULL을 반환하게 됩니다.
    • 함수는 추출된 토큰의 시작을 가리키는 포인터를 반환합니다.
  2. 이후 호출 (strNULL인 경우)

    • strtok은 이전 호출에서 내부적으로 저장된 위치부터 검색을 시작합니다.
    • 동작 방식은 첫 번째 호출과 동일하며, 다음 토큰을 찾아 반환합니다.

반환 값

다음 토큰의 시작을 가리키는 포인터를 반환합니다. 더 이상 토큰이 없으면 NULL을 반환합니다.

중요 사항 및 주의점

  • 원본 문자열 수정 (파괴적 함수): strtok 함수는 원본 문자열의 구분자 위치에 널 문자('\0')를 삽입하여 토큰을 분리합니다. 따라서 원본 문자열이 변경되므로, 문자열 리터럴(예: "Hello World")을 str 인수로 직접 전달하면 안 됩니다. 반드시 쓰기 가능한 문자 배열을 사용해야 합니다.
  • 스레드 안전성 (Thread Safety) 문제: strtok은 내부적으로 정적 변수를 사용하여 상태를 유지합니다. 이 때문에 여러 스레드에서 동시에 strtok을 호출하면 예상치 못한 동작을 하거나 경쟁 조건(race condition)이 발생할 수 있습니다. 멀티스레드 환경에서는 strtok_r (reentrant version)과 같은 스레드 안전한 대안을 사용하는 것이 권장됩니다.
  • 유연한 구분자: strtok은 각 호출마다 다른 구분자 문자열을 사용할 수 있습니다.

코드 예제 1: 문장 토큰화

공백 문자를 구분자로 사용하여 문장을 단어 단위로 분리하는 예제입니다.

#include <cstring>  // strtok 함수를 위해 필요
#include <iostream> // 표준 입출력을 위해 필요

int main() {
    char sentenceBuffer[100] = "오늘도 하늘은 맑고 바람은 시원합니다."; // 원본 문자열 버퍼
    const char* spaceDelimiter = " "; // 공백 문자를 구분자로 사용

    std::cout << "원본 문자열: '" << sentenceBuffer << "'\n";
    std::cout << "분리된 토큰:\n";

    char* currentToken = std::strtok(sentenceBuffer, spaceDelimiter); // 첫 번째 토큰 추출

    // 토큰이 더 이상 없을 때까지 반복
    while (currentToken != nullptr) { // C++11부터 NULL 대신 nullptr 권장
        std::cout << "  - " << currentToken << '\n';
        currentToken = std::strtok(nullptr, spaceDelimiter); // 다음 토큰 추출 (NULL 전달)
    }

    std::cout << "\nstrtok 호출 후 원본 배열 (수정됨): '" << sentenceBuffer << "'\n";
    // 원본 배열이 "오늘도\0하늘은..." 과 같이 변경되었기 때문에,
    // 여기서 출력되는 내용은 첫 번째 토큰인 "오늘도"까지만 보일 수 있습니다.
    return 0;
}

출력:

원본 문자열: '오늘도 하늘은 맑고 바람은 시원합니다.'
분리된 토큰:
  - 오늘도
  - 하늘은
  - 맑고
  - 바람은
  - 시원합니다.

strtok 호출 후 원본 배열 (수정됨): '오늘도'

코드 예제 2: 사용자 계정 정보 분리

콜론(:)을 구분자로 사용하여 사용자 이름과 비밀번호를 분리하는 예제입니다.

#include <stdio.h> // 표준 입출력 함수 (printf)
#include <string.h> // 문자열 처리 함수 (strtok)

int main() {
    char accountCredential[] = "user_alpha:secret_pass123"; // 계정 정보 문자열
    char *extractedUsername;
    char *extractedPassword;
    const char* colonSeparator = ":"; // 구분자 콜론

    printf("원본 계정 정보: %s\n", accountCredential);

    // strtok을 사용하여 사용자 이름과 비밀번호를 분리
    // 첫 번째 호출: 원본 문자열과 구분자 전달
    extractedUsername = strtok(accountCredential, colonSeparator);
    // 두 번째 호출: NULL을 전달하여 이전 호출의 다음 지점부터 계속 탐색
    extractedPassword = strtok(NULL, colonSeparator);

    if (extractedUsername != NULL && extractedPassword != NULL) {
        printf("분리된 사용자 이름: %s\n", extractedUsername);
        printf("분리된 비밀번호: %s\n", extractedPassword);
    } else {
        printf("계정 정보 형식이 올바르지 않습니다!\n");
    }

    printf("\nstrtok 호출 후 원본 배열 (수정됨): %s\n", accountCredential);
    // 첫 번째 ':' 위치가 널 문자로 바뀌었으므로 "user_alpha"까지만 출력됩니다.

    return 0;
}

출력:

원본 계정 정보: user_alpha:secret_pass123
분리된 사용자 이름: user_alpha
분리된 비밀번호: secret_pass123

strtok 호출 후 원본 배열 (수정됨): user_alpha

태그: C C++ strtok string tokenization cstring

7월 23일 03:06에 게시됨