[C] strdup, strjoin, split 구현
본문 바로가기

C

[C] strdup, strjoin, split 구현

728x90
반응형

stdup의 경우 기존에 C에 있던 함수이기 때문에 많은 분들이 익숙하시겠지만
 
join과 split의 경우 C언어에 없는 함수이기 때문에 많이 생소하실 겁니다.
 
다른 언어에서 주로 사용되는 join과 split의 경우 문자열을 이어주고, 특정 문자 기준으로 나누어 주고 해주는 함수이죠
좀 더 자세히 알고 싶으시면 파이썬 join과 split에 대해서 검색해 보시면 됩니다.
 

Strdup


#include <stdlib.h> 	const char *(const char *s1);

함수 내에서 힙메모리에 s1 문자열 크기의 공간을 할당 후 문자열을 복사시키고 반환해주는 함수입니다.
 

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

const char *my_strdup(const char *s1) {
    char *ret;
    int len = strlen(s1); // s1 string length

    ret = (char *)malloc(len + 1); // allocate memory
    if (ret == NULL) { return NULL; } // null guard

    for (int i = 0; i < len; i++) { ret[i] = s1[i]; }
    ret[len] = 0;

    return (const char*)ret;
}

int main(int ac, char *av[]) {
    const char *str = my_strdup(av[1]);
    if (str == NULL) { return 0; }

    printf("%s\n", str);
    free((void *)str);

    return 0;
}

strlen으로 길이를 받고 그 길이만큼 공간을 할당 후 for문으로 길미만큼 복사를 반복시켜주고 반환하면 끝입니다.
 
 

Strjoin


const char *strjoin(const char *s1, const char *s2);

 
두 문자열을 이어붙여 주는 strjoin은 C언어에서 지원해 주지 않는 함수이기에 man으로 검색해도 나오지 않습니다.
저희가 만들 strjoin함수의 형태는 아래와 같습니다.
 

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

const char *strjoin(const char *s1, const char *s2) {
    char *ret;

    int len1 = strlen(s1); // s1 length
    int len2 = strlen(s2); // s2 length
    ret = (char *)malloc(len1 + len2 + 1); // allocate memory
    if (ret == NULL) { return NULL; } // null guard

    for (int i = 0; i < len1; i++) { ret[i] = s1[i]; }
    for (int i = 0; i < len2; i++) { ret[i + len1] = s2[i]; }
    ret[len1 + len2] = 0;

    return ret;
}

int main(int ac, char *av[]) {
    const char *str = strjoin(av[1], av[2]);
    if (str == NULL) { return 0; }

    printf("%s\n", str);
    free((void *)str);

    return 0;
}

strdup 구현과 비슷합니다
두 문자열 총 크기만큼 할당하고 s1, s2를 차례대로 복사시켜주면 간단하게 구현할 수 있습니다.
 

Split


split 함수도 strjoin과 마찬가지로 C언어에서 지원해 주지 않는 함수입니다.
구현 방법은 아래와 같습니다.
 

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// free allocated memory
const char **free_all(char **s) {
    for (int i = 0; s[i]; i++) { free(s[i]); }
    free(s);

    return NULL;
}

int count_units(const char *s1, int c) {
    int ret = 0;
    int i = 0;
    
    while (s1[i]) {
        if (s1[i] == c) {
            while (s1[i] && s1[i] == c) { i++; }
        } else {
            ret++; // count unit
            while (s1[i] && s1[i] != c) { i++; }
        }
    }
    return ret;
}

char *get_unit(const char *s1, int c, int *i) {
    char    *ret;
    int     pos, j = 0;

    while (s1[*i] && s1[*i] == c) { *i += 1; }
    pos = *i; // flag start unit pos
    while (s1[*i] && s1[*i] != c) { *i += 1; }

    ret = (char *)malloc(*i - pos + 1);
    if (ret == NULL) { return NULL; }

    while (pos != *i) { ret[j++] = s1[pos++]; }
    ret[j] = 0;
    return ret;
}

const char **split(const char *s1, int c) {
    char **ret;
    int idx = 0;

    int cnt = count_units(s1, c);
    ret = (char **)malloc(sizeof(char *) * (cnt + 1));
    if (ret == NULL) { return NULL; }
    ret[cnt] = NULL;

    for (int i = 0; i < cnt; i++) {
        ret[i] = get_unit(s1, c, &idx);
        if (ret[i] == NULL) { return free_all(ret); }
    }

    return (const char **)ret;
}

int main(int ac, char *av[]) {
    const char **str = split(av[1], av[2][0]);
    if (str == NULL) { return 0; }

    for (int i = 0; str[i]; i++) {
        printf("%s\n", str[i]);
    }

    free_all((char **)str);
    return 0;
}

맨 처음 count units 함수로 총 몇개로 문자열이 나뉘는지 파악해준뒤
get unit 함수로 파악한 개수만큼 반복해서 문자열을 파싱해 주면 됩니다.
 
만약 문자 하나가 아닌 문자열을 기준으로 split하고 싶다면
count units 부분과 get unit 함수에서 strstr 함수를 적절히 사용하면 됩니다.

728x90
반응형