개요
C 및 C++ 표준 라이브러리에서 제공하는 strtok 함수는 널(null)로 끝나는 문자열을 지정된 구분자(delimiter)를 기준으로 여러 개의 '토큰(token)'으로 분리하는 데 사용됩니다. 이 함수는 주로 C 스타일 문자열(`char*`)을 다룰 때 유용하며, <cstring> (C++에서는 <string.h>) 헤더 파일에 정의되어 있습니다.
함수 시그니처
char* strtok( char* str, const char* delim );
str: 분리할 문자열을 가리키는 포인터입니다. 첫 번째 호출 시에는 원본 문자열을 전달하고, 이후 호출에서는NULL을 전달합니다.delim: 토큰을 구분하는 데 사용될 하나 이상의 문자를 포함하는 널 종료 문자열을 가리키는 포인터입니다.
동작 방식
strtok 함수는 호출될 때마다 다음 토큰을 찾아 반환하며, 내부적으로 정적(static) 변수를 사용하여 이전 호출의 상태를 기억합니다. 따라서 여러 번 호출하여 동일한 문자열에서 연속적인 토큰을 추출할 수 있습니다.
첫 번째 호출 (
str이NULL이 아닌 경우)strtok은str에서delim에 포함되지 않는 첫 번째 문자를 찾습니다. 이 문자가 토큰의 시작점이 됩니다.- 만약 이러한 문자를 찾을 수 없다면,
str에 토큰이 없다는 의미이므로NULL을 반환합니다. - 토큰 시작점을 찾으면, 그 지점부터
delim에 포함된 첫 번째 문자를 검색합니다. - 이 구분자를 찾으면, 해당 구분자 위치를 널 문자(
'\0')로 교체하여 현재 토큰을 종료시키고, 다음 토큰이 시작될 위치를 내부적으로 저장합니다. - 구분자를 찾지 못하면,
str의 나머지 부분이 하나의 토큰이 되며, 이후strtok호출은NULL을 반환하게 됩니다. - 함수는 추출된 토큰의 시작을 가리키는 포인터를 반환합니다.
이후 호출 (
str이NULL인 경우)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