Info
ft_split is a function that splits a string into an array of substrings based on a specified delimiter character. It allocates memory dynamically for each substring and the array, ensuring the result is null-terminated.
Walkthrough
Counting words
First, we have to create our ft_split
function with few variables:
char **result
for two-dimensional arrayint i
to iterate through theresult
array
char **ft_split(char const *s, char c)
{
char **result;
int i;
i = 0;
// ...
}
Now, if it was a one-dimensional array, we would just count the characters of s
and allocate that much of memory. But in our case we will have to count each word in s
splitted by c
. Therefore, lets create a count_words
function:
Info
The
static
keyword in front of a function in C limits its scope to the file where it is declared.
static int count_words(const char *str, char delim)
{
int count;
// ...
}
The word-counting logic should follow these steps:
- Skip each delimeter
c
- When a character is not equal to the delimiter
c
:
- Increment the word count.
- Move the pointer until the next delimiter to locate the next word.
Therefore, the
count_words
function should be implemented as follows:
static int count_words(const char *str, char delim)
{
int count;
count = 0;
// for each valid character
while (*str)
{
// skip all delimeters
while (*str && is_delim(*str, delim))
str++;
// if not a delimeter
if (*str)
{
// increment the count
count++;
// skip all characters until the next delimeter
while (*str && !is_delim(*str, delim))
str++;
// repeat 🔁
}
}
return (count);
}
Now, we can use the count_words
function to allocate memory for the 2D array.
Creating the 2D Array
Let’s use the count_words
function to allocate memory for each word, including space for the NULL
terminator:
char **ft_split(char const *s, char c)
{
char **result;
int i;
i = 0;
// terminate early if pointer "s" isn't valid
if (!s)
return (NULL);
// Array of Pointers, 2D Array
result = (char **)malloc((count_words(s, c) + 1) * sizeof(char *));
if (!result)
return (NULL);
// ...
Word Allocation
Now that we have our array of pointers allocated, we need to handle the allocation of individual words. Let’s create an alloc_word
function to manage this:
static char *alloc_word(const char *str, char delim)
{
int len;
char *word;
int i;
len = 0;
i = 0;
// Count characters until delimiter
while (str[len] && !is_delim(str[len], delim))
len++;
// Allocate memory for the word + null terminator
word = (char *)malloc((len + 1) * sizeof(char));
if (!word)
return (NULL);
// Copy characters
while (i < len)
{
word[i] = str[i];
i++;
}
// Add null terminator
word[len] = '\0';
return (word);
}
Helper Functions
free_all
Frees All Allocated Memory in Case of an Error
static void free_all(char **result, int i)
{
// free each substring starting from current index
while (i >= 0)
free(result[i--]);
// free the 2D array itself
free(result);
}
is_delim
Compares the given char with the delimeter
static int is_delim(char c, char delim)
{
return (c == delim);
}
Putting Everything Together
char **ft_split(char const *s, char c)
{
char **result;
int i;
i = 0;
if (!s)
return (NULL);
result = (char **)malloc((count_words(s, c) + 1) * sizeof(char *));
if (!result)
return (NULL);
while (*s)
{
// skip all delimeters
while (*s && is_delim(*s, c))
s++;
if (*s)
{
// allocate and fill the memory for current word
// using pointer "s"
result[i] = alloc_word(s, c);
if (!result[i])
// free all memory in case of an error
return (free_all(result, i - 1), NULL);
// increase the index in order to write the next string
i++;
// skip all characters until the next delimeter
while (*s && !is_delim(*s, c))
s++;
// repeat 🔁
}
}
result[i] = NULL;
return (result);
}
Final Code
#include <stdlib.h>
static int is_delim(char c, char delim)
{
return (c == delim);
}
static int count_words(const char *str, char delim)
{
int count;
count = 0;
while (*str)
{
while (*str && is_delim(*str, delim))
str++;
if (*str)
{
count++;
while (*str && !is_delim(*str, delim))
str++;
}
}
return (count);
}
static char *alloc_word(const char *str, char delim)
{
int len;
char *word;
int i;
len = 0;
i = 0;
while (str[len] && !is_delim(str[len], delim))
len++;
word = (char *)malloc((len + 1) * sizeof(char));
if (!word)
return (NULL);
while (i < len)
{
word[i] = str[i];
i++;
}
word[len] = '\0';
return (word);
}
static void free_all(char **result, int i)
{
while (i >= 0)
free(result[i--]);
free(result);
}
char **ft_split(char const *s, char c)
{
char **result;
int i;
i = 0;
if (!s)
return (NULL);
result = (char **)malloc((count_words(s, c) + 1) * sizeof(char *));
if (!result)
return (NULL);
while (*s)
{
while (*s && is_delim(*s, c))
s++;
if (*s)
{
result[i] = alloc_word(s, c);
if (!result[i])
return (free_all(result, i - 1), NULL);
i++;
while (*s && !is_delim(*s, c))
s++;
}
}
result[i] = NULL;
return (result);
}