strtok 함수의 이해와 활용
1. 기본 사용법 및 작동 원리
strtok는 C 언어에서 제공하는 문자열 토큰화 함수로, 지정된 구분자를 기준으로 문자열을 조각내는 데 사용됩니다. 이 함수는 <string.h> 헤더에 정의되어 있으며, 반복 호출을 통해 연속적인 토큰을 추출할 수 있습니다.
함수 시그니처
char *strtok(char *str, const char *delim);
- str: 첫 번째 호출 시 분석할 문자열 포인터. 이후 호출에서는 NULL을 전달하여 이전 상태를 이어갑니다.
- delim: 하나 이상의 구분자 문자로 구성된 문자열.
- 반환값: 현재 추출된 토큰의 시작 주소. 더 이상 토큰이 없으면 NULL 반환.
기본 예제
#include <stdio.h>
#include <string.h>
int main() {
char input[] = "apple:banana,orange;grape";
const char* delimiters = ":,;";
char* token;
// 첫 번째 토큰 추출
token = strtok(input, delimiters);
while (token != NULL) {
printf("토큰: %s\n", token);
token = strtok(NULL, delimiters); // 계속 진행
}
return 0;
}
출력 결과:
토큰: apple 토큰: banana 토큰: orange 토큰: grape
2. 주의사항 및 제한점
원본 문자열 수정
strtok는 내부적으로 구분자를 \0으로 치환하여 문자열을 파괴적으로 분할합니다. 따라서 리터럴 문자열이나 읽기 전용 메모리에 접근할 경우 프로그램 충돌이 발생할 수 있습니다.
다음은 잘못된 사용 예입니다:
char *literal = "one-two-three";
strtok(literal, "-"); // 위험: 문자 상수 영역 수정 시도
올바른 방법은 가변 배열을 사용하는 것입니다:
char buffer[] = "one-two-three";
strtok(buffer, "-"); // 안전: 스택에 할당된 수정 가능한 배열
다중 구분자 처리
두 번째 인자 delim은 단일 문자가 아니라 문자 집합일 수 있습니다. 예를 들어 ",;:"처럼 여러 종류의 구분자를 동시에 처리할 수 있습니다.
선두/후미 구분자 무시
입력 문자열의 시작 또는 끝에 구분자가 있어도 strtok는 이를 자동으로 건너뛰고 유효한 토큰부터 반환합니다. 예를 들어 "-aaa-bbb-"는 첫 번째 토큰으로 "aaa"를 반환하며, 선두의 -는 무시됩니다.
3. 내부 동작 원리와 재구현
strtok는 정적(static) 변수를 이용해 이전 호출 상태를 유지합니다. 아래는 표준 라이브러리와 유사하게 동작하는 사용자 정의 구현입니다.
char* my_strtok(char* src, const char* delim) {
static char* saved_pos = NULL;
// 새로운 문자열 시작 또는 이전 상태 복원
if (src != NULL) {
saved_pos = src;
} else if (saved_pos == NULL) {
return NULL;
}
// 구분자로 시작하는 부분 건너뛰기
while (*saved_pos && strchr(delim, *saved_pos)) {
++saved_pos;
}
// 더 이상 문자가 없을 경우
if (*saved_pos == '\0') {
return NULL;
}
char* start = saved_pos;
// 다음 구분자까지 이동
while (*saved_pos && !strchr(delim, *saved_pos)) {
++saved_pos;
}
// 구분자 위치를 널 문자로 대체
if (*saved_pos) {
*saved_pos = '\0';
++saved_pos;
}
return start;
}
이 구현은 정적 포인터 saved_pos를 통해 상태를 보존하며, strchr을 사용해 구분자 존재 여부를 확인합니다. 실제 glibc 구현과 유사한 방식으로, 호출 간 상태를 유지하면서 안전하게 토큰을 분리합니다.
대안 함수 고려
strtok는 스레드 비안전하고 원본 문자열을 변경한다는 단점이 있어, 현대 코드에서는 다음과 같은 대안을 권장합니다:
strtok_r: 재진입 가능 버전 (POSIX)strsep: 더 유연한 토큰 분리 함수- 직접 구현:
strspn,strcspn등을 조합
특히 멀티스레드 환경에서는 정적 상태를 사용하지 않는 strtok_r 사용이 필수적입니다.