[C언어] 특정 문자로 문자열 자르기 - Split 구현
- 컴퓨터공학과/Programming
- 2023. 2. 25.
C언어에는 문자열을 쉽게 Split하는 내장함수가 없습니다.
그래서 열받죠...😩 아니 str.split(",") 하면 알아서 딱 되어야하는 거 아니냐~~!
C언어 사용하면서 가장 불편한 부분이 String 관련해서이지않나 싶어요.
자주 쓰이는 코드이기에 기록할 겸 포스팅!
★ 최종 목표 : C언어에서 사용할 수 있는 Split함수 구현
→ 바로 보실 분은 맨 아래쪽으로
특정 구분자를 기준으로 문자열 분할
문자열을 분할하는 방법으로는 문자열에서 특정 구분자(delimiter)를 기준으로 분할하는 방법이 있습니다.
strtok 함수를 사용하는 것인데요, 우선 예제 코드 보시죠
예제
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple,banana,cherry,date";
char* token = strtok(str, ","); // ","를 구분자로 사용하여 문자열 분할
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ","); // 다음 분할 위치로 이동
}
return 0;
}
위 코드는 "apple,banana,cherry,date" 문자열에서 ","를 구분자로 사용하여 문자열을 분할합니다.
strtok 함수를 사용하여 구분자를 기준으로 첫 번째 토큰을 추출한 뒤, while문을 이용해 모든 토큰을 출력합니다.
strtok 함수는 이전 호출에서 마지막으로 발견된 토큰 이후부터 다음 토큰을 찾습니다.
출력 결과
위 코드의 출력 결과는 다음과 같습니다.
apple
banana
cherry
date
strtok 함수 설명
함수 원형 및 설명
char* strtok(char* str, const char* delim);
strtok 함수는 첫 번째 매개변수로 문자열 포인터(str)를 받고, 두 번째 매개변수로 구분자(delimiter)를 받습니다.
strtok 함수는 첫 번째 호출 시 문자열 포인터(str)가 가리키는 문자열에서 구분자(delimiter)를 찾아 구분자 이전까지의 문자열을 반환합니다. 이후, 다음 호출에서는 이전 호출에서 반환한 문자열 이후의 문자열에서 구분자를 찾아 반환합니다. 이를 반복하여 문자열의 모든 구분자를 기준으로 분할할 수 있습니다.
예를 들어 "apple,banana,cherry,date" 문자열에서 ","를 구분자로 사용하여 문자열을 분할하면 다음과 같은 과정이 이루어집니다.
1) 첫 번째 호출: "apple,banana,cherry,date"에서 첫 번째 "," 이전까지의 문자열 "apple"을 반환합니다.
* 이때, 원래 문자열은 "apple,banana,cherry,date"에서 "apple"이 제거된 "banana,cherry,date"가 됩니다.
2) 두 번째 호출: "banana,cherry,date"에서 첫 번째 "," 이전까지의 문자열 "banana"을 반환합니다.
* 이때, 원래 문자열은 "banana,cherry,date"에서 "banana"가 제거된 "cherry,date"가 됩니다.
3) 세 번째 호출: "cherry,date"에서 첫 번째 "," 이전까지의 문자열 "cherry"을 반환합니다.
* 이때, 원래 문자열은 "cherry,date"에서 "cherry"가 제거된 "date"가 됩니다.
4) 네 번째 호출: "date"에서 첫 번째 "," 이전까지의 문자열 "date"을 반환합니다.
5) 이후, 더 이상 ","이 없으므로 NULL을 반환하고 strtok 함수 호출이 종료됩니다.
함수 헤더
strtok 함수는 C 표준 라이브러리의 <string.h> 헤더 파일에 선언되어 있습니다.
따라서 strtok 함수를 사용하기 위해서는 해당 헤더 파일을 프로그램에 포함시켜야 합니다.
아래와 같이 프로그램 파일의 맨 위에 string.h 헤더 파일을 포함시키면 됩니다.
#include <string.h>
strtok 주의할 점
주의점
strtok 함수를 사용할 때 주의해야 할 점이 있습니다.
1) 예제에서도 볼 수 있듯이 strtok 함수는 원래 문자열을 변경합니다. 이 때문에 strtok 함수를 사용하면 원래 문자열이 손상될 수 있습니다.
2) strtok 함수는 연속된 구분자를 처리할 때 빈 문자열도 토큰으로 취급합니다.
예를 들어 "apple,,banana"와 같은 문자열에서 ","를 구분자로 사용하면 빈 문자열도 토큰으로 인식됩니다.
3) strtok 함수는 내부적으로 정적 변수(static variable)를 사용하기 때문에 여러 스레드에서 동시에 호출하면 예기치 않은 결과가 발생할 수 있습니다.
해결 방법
이러한 문제를 해결하려면 strtok_r 함수를 사용하면 됩니다. strtok_r 함수는 원래 문자열을 변경하지 않고, delimiter가 연속해서 나타나는 경우 빈 문자열을 토큰으로 처리하지 않습니다. 또한 strtok_r 함수는 스레드 안전성을 위해 호출 간 상태를 유지하기 위한 포인터 변수를 인수로 전달합니다. 따라서 여러 스레드에서 동시에 호출해도 안전하게 사용할 수 있습니다.
strtok_r 함수 설명
strtok_r 함수는 strtok 함수와 거의 비슷하지만, 두 가지 매개변수가 추가됩니다. strtok_r 함수의 원형은 다음과 같습니다.
char* strtok_r(char* str, const char* delim, char** saveptr);
첫 번째 매개변수와 두 번째 매개변수는 strtok 함수와 동일합니다. 그러나 세 번째 매개변수는 이전에 처리한 문자열을 저장하기 위한 포인터를 전달합니다. 이 포인터는 strtok_r 함수의 호출 간에 유지됩니다. 따라서 strtok_r 함수는 여러 문자열을 동시에 처리할 수 있습니다.
strtok_r 함수를 사용하여 "apple,banana,cherry,date" 문자열에서 ","를 구분자로 사용하여 문자열을 분할하면 다음과 같은 과정이 이루어집니다.
★ 최종 목표 : Split함수
C 언어에는 기본적으로 문자열을 분리하는 split 함수가 없지만, 직접 구현하여 사용할 수 있습니다.
아래는 Split 함수 입니다.
이 예시에서는 문자열을 구분자(delimiter)를 기준으로 분리한 후, 분리된 문자열들을 문자열 배열(String[]) 형태로 반환합니다. 이 때 문자열의 개수는 count 변수에 담습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char** split(char* str, const char* delimiter, int* count) {
int i, j, len;
char* token;
char** result = NULL;
// 구분자로 문자열을 분리한 후, 문자열 개수(count)를 구합니다.
token = strtok(str, delimiter);
while (token != NULL) {
(*count)++;
result = (char**)realloc(result, (*count) * sizeof(char*));
result[(*count) - 1] = token;
token = strtok(NULL, delimiter);
}
// 문자열 개수(count)만큼의 문자열 배열을 동적으로 할당합니다.
result = (char**)realloc(result, (*count) * sizeof(char*));
// 문자열 배열에 분리된 문자열을 복사합니다.
len = strlen(str);
j = 0;
for (i = 0; i < (*count); i++) {
len -= strlen(result[i]) + strlen(delimiter);
strncpy(result[i], str + j, strlen(result[i]));
j += strlen(result[i]) + strlen(delimiter);
result[i][strlen(result[i])] = '\0';
}
return result;
}
int main() {
char str[] = "apple,banana,cherry,date";
const char delimiter[] = ",";
int count = 0;
char** arr = split(str, delimiter, &count);
for (int i = 0; i < count; i++) {
printf("%s\n", arr[i]);
}
// 동적으로 할당한 문자열 배열 메모리를 해제합니다.
for (int i = 0; i < count; i++) {
free(arr[i]);
}
free(arr);
return 0;
}
** 위 예시 코드는 문자열 배열을 동적으로 할당하여 사용하기 때문에, 사용 후에는 반드시 할당한 메모리를 해제해주어야 합니다.
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
apple
banana
cherry
date
아주 쉽죠~!!!
복붙해서 기본 내장함수처럼 편하게 사용하세요!
Free!! 메모리 해제하는 것 잊지말기!
'컴퓨터공학과 > Programming' 카테고리의 다른 글
[고급 C++] 매크로함수 (0) | 2020.11.02 |
---|---|
[고급 C++] 비트연산자 (0) | 2020.11.02 |
[고급 C++] 공용체와 구조체 비트필드 + 바이트 오더링 / 활용방안 (1) | 2020.11.02 |
[고급 C++] 구조체와 구조체포인터 (0) | 2020.10.27 |
[고급 C++] 가변인자 / CERT C (feat. 안전 코딩하기) (1) | 2020.10.26 |