1

I am trying to create a function that will accept a string, and return an array of words in the string. Here is my attempt:

#include "main.h"

/**
 * str_split - Splits a string
 * @str: The string that will be splited
 * 
 * Return: On success, it returns the new array
 * of strings. On failure, it returns NULL
 */
char **str_split(char *str)
{   
    char *piece, **str_arr = NULL, *str_cpy;
    int number_of_words = 0, i;

    if (str == NULL)
    {
        return (NULL);
    }
    str_cpy = str;
    piece = strtok(str_cpy, " ");
    while (piece != NULL)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        number_of_words++;
        piece = strtok(NULL, " ");
    }
    
    str_arr = (char **)malloc(sizeof(char *) * number_of_words);
    piece = strtok(str, " ");
    for (i = 0; piece != NULL; i++)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        str_arr[i] = (char *)malloc(sizeof(char) * (strlen(piece) + 1));
        strcpy(str_arr[i], piece);
        piece = strtok(NULL, " ");
    }
    return (str_arr);
}

Once I compile my file, I should be getting:

Hello
World

But I am getting:

Hello

Why is this happening? I have tried to dynamically allocate memory for the new string array, by going through the copy of the original string and keeping track of the number of words. Is this happening because the space allocated for the array of strings is not enough?

Leuel Asfaw
  • 316
  • 1
  • 14
  • 1
    Your program initializes `str_arr` to a null pointer and never assigns it any other value. So using `str_arr[i]` attempts to dereference that null pointer. – Eric Postpischil Sep 15 '22 at 12:27
  • When you malloc strings, you need one additional byte for the null terminator. Or simply use strdup instead of malloc/strcpy. – jarmod Sep 15 '22 at 12:36
  • For an alternative approach, see [this course notes chapter](https://www.eskimo.com/~scs/cclass/notes/sx10h.html). – Steve Summit Sep 15 '22 at 14:35
  • If you try to go through the input string with `strtok` twice, once to count the words and then a second time (after allocating space) to actually extract the words, you're going to have the problem that the first pass modified the string (by inserting `\0`'s), so the second pass is probably only going to find one word. – Steve Summit Sep 15 '22 at 14:37

1 Answers1

2

The code seems fine overall, with just some issues:

You tried to copy str, as strtok modifies it while parsing. This is the right approach. However, the following line is wrong:

str_cpy = str;

This is not a copy of strings, it is only copying the address of the string. You can use strdup function here.

Also, you need to return the number of words counted otherwise the caller will not know how many were parsed.

Finally, be careful when you define the string to be passed to this function. If you call it with:

char **arr = str_split ("Hello World", &nwords);

Or even with:

char *str = "Hello World";
char **arr = str_split (str, &nwords);

The program will crash as str here is read-only (see this).

Taking care of these, the program should work with:

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

/**
 * str_split - Splits a string
 * @str: The string that will be splited
 * 
 * Return: On success, it returns the new array
 * of strings. On failure, it returns NULL
 */
char **str_split(char *str, int *number_of_words)
{
    char *piece, **str_arr = NULL, *str_cpy = NULL;
    int i = 0;

    if (str == NULL)
    {
        return (NULL);
    }
    str_cpy = strdup (str);
    piece = strtok(str_cpy, " ");
    while (piece != NULL)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        (*number_of_words)++;
        piece = strtok(NULL, " ");
    }

    str_arr = (char **)malloc(sizeof(char *) * (*number_of_words));
    piece = strtok(str, " ");
    for (i = 0; piece != NULL; i++)
    {
        if ((*piece) == '\n')
        {
            piece = strtok(NULL, " ");
            continue;
        }
        str_arr[i] = (char *)malloc(sizeof(char) * (strlen(piece) + 1));
        strcpy(str_arr[i], piece);
        piece = strtok(NULL, " ");
    }

    if (str_cpy)
        free (str_cpy);

    return (str_arr);
}

int main ()
{
    int nwords = 0;
    char str[] = "Hello World";

    char **arr = str_split (str, &nwords);

    for (int i = 0; i < nwords; i++) {

        printf ("word %d: %s\n", i, arr[i]);
    }

    // Needs to free allocated memory...
}

Testing:

$ gcc main.c && ./a.out 
word 0: Hello
word 1: World
Jardel Lucca
  • 1,115
  • 3
  • 10
  • 19