I wrote this after using strtok_r
and needing something similar that can copy the input string and handle multiple characters in the delimiter. Its functionally similar to Python's str.split()
, as it keeps empty spaces without trimming.
tokens.h
#ifndef TOKENS_H
#define TOKENS_H
#include <string.h>
struct Tokens {
char **array;
size_t count;
};
extern struct Tokens *string_split(const char *input,
const char *delim,
const int count);
extern void tokens_free(struct Tokens *tokens);
#endif
tokens.c
#include "tokens.h"
#include <stdbool.h>
#include <stdlib.h>
static bool tokens_add(struct Tokens *tokens, const char *token){
tokens->array = realloc(tokens->array,
++tokens->count * sizeof(char *));
if (!tokens->array){
tokens->count--;
return false;
}
char *dupstr = strdup(token);
if (!dupstr){
tokens->count--;
return false;
}
tokens->array[tokens->count - 1] = dupstr;
return true;
}
struct Tokens *string_split(const char *input,
const char *delim,
const int count){
struct Tokens *tokens = malloc(sizeof(struct Tokens));
if (!tokens){
return NULL;
}
tokens->count = 0;
tokens->array = NULL;
const size_t inputlen = strlen(input);
const size_t delimlen = strlen(delim);
if (count == 0 || delimlen == 0){
tokens_add(tokens, input);
return tokens;
}
int delimcount = 0;
size_t inputpos = 0;
const char *start = NULL;
while ((start = strstr(&input[inputpos], delim))){
const int delimpos = (start - &input[inputpos]);
char token[delimpos + 1];
memcpy(token, &input[inputpos], delimpos);
token[delimpos] = '\0';
if (!tokens_add(tokens, token)){
return NULL;
}
inputpos += (delimpos + delimlen);
if (delimcount++ == count){
break;
}
}
if (inputpos <= inputlen){
const int charcount = (inputlen - inputpos);
char token[charcount + 1];
memcpy(token, &input[inputpos], charcount);
token[charcount] = '\0';
if (!tokens_add(tokens, token)){
return NULL;
}
}
return tokens;
}
void tokens_free(struct Tokens *tokens){
if (!tokens){
return;
}
for (size_t index = 0; index < tokens->count; index++){
free(tokens->array[index]);
}
free(tokens);
}
test.c
#include <stdio.h>
#include "tokens.h"
int main(void){
// <0 = split indefinitely
struct Tokens *tokens = string_split("Hello;world;!;", ";", -1);
if (tokens){
for (size_t index = 0; index < tokens->count; index++){
printf("%s\n", tokens->array[index]);
}
tokens_free(tokens);
}
}