0

I'm trying to divide a string of alphabetically sorted words char *str = "a/apple/arm/basket/bread/car/camp/element/..." into an array of strings alphabetically like so:

arr[0] = "a/apple/arm"
arr[1] = "basket/bread"
arr[2] = "car/camp"
arr[3] = ""
arr[4] = "element"
...

I'm not very skilled in C, so my approach was going to be to declare:

char arr[26][100]; 
char curr_letter = "a";

and then iterate over each char in the string looking for "/" follow by char != curr_letter, then strcpy that substring to the correct location.

I'm not sure if my approach is very good, let alone how to implement it properly. Any help would be greatly appreciated!

  • 1
    Why are you looking for "\" in a String which clearly doesn't contain it? – Diaco Jan 22 '22 at 16:14
  • From my understanding, you are supposed to add each word to one of the elements. Words starting with letter 'a' go to arr[0], words starting with letter 'b' go into arr[1] and so on... I think author should think about this task a bit more to formulate specific questions. – Diaco Jan 22 '22 at 16:39
  • "basket/bread" should be "basket", "bread" (2 elts) right? – Example person Jan 22 '22 at 16:53
  • Can you assume that the string pointed to by `str` will end with a `/`? Or can you assume that it will not end with a `/`? Or should your program be able to handle both cases? – Andreas Wenzel Jan 22 '22 at 19:56

2 Answers2

1

So we basically loop through the string, and check if we found the "split character' and we also check that we didn't find the 'curr_letter' as the next character.

We keep track of the consumed length, the current length (used for memcpy later to copy the current string to the array).

When we find a position where we can add the current string to the array, we allocate space and copy the string to it as the next element in the array. We also add the current_length to consumed, and the current_length is reset.

We use due_to_end to find out if we have a / in the current string, and remove it accordingly.

Try:

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

int main() {
        char *str = "a/apple/arm/basket/bread/car/camp/element/...";
        char split_char = '/';
        char nosplit_char = 'a';

        char **array = NULL;
        int num_elts = 0;

        // read all the characters one by one, and add to array if 
        // your condition is met, or if the string ends
        int current_length = 0; // holds the current length of the element to be added
        int consumed = 0; // holds how much we already added to the array
        for (int i = 0; i < strlen(str); i++) { // loop through string
                current_length++; // increment first
                int due_to_end = 0;
                if ( ( str[i] == split_char // check if split character found
                    && ( i != (strlen(str) - 1) // check if its not the end of the string, so when we check for the next character, we don't overflow
                    && str[i + 1] != nosplit_char ) ) // check if the next char is not the curr_letter(nosplit_char)
                   || (i == strlen(str) - 1 && (due_to_end = 1))) { // **OR**, check if end of string
                        array = realloc(array, (num_elts + 1) * sizeof(char *)); // allocate space in the array
                        array[num_elts] = calloc(current_length + 1, sizeof(char)); // allocate space for the string
                        memcpy(array[num_elts++], str + consumed, (due_to_end == 0 ? current_length - 1 : current_length)); // copy the string to the current array offset's allocated memory, and remove 1 character (slash) if this is not the end of the string

                        consumed += current_length; // add what we consumed right now
                        current_length = 0; // reset current_length
                }
        }

        for (int i = 0; i < num_elts; i++) { // loop through all the elements for overview
                printf("%s\n", array[i]);
                free(array[i]);
        }
        free(array);
}
Example person
  • 3,198
  • 3
  • 18
  • 45
  • I doubt that OP will learn much if you simply provide the solution to OP. You may want to read this: [How do I ask and answer homework questions?](https://meta.stackoverflow.com/q/334822/12149471) – Andreas Wenzel Jan 22 '22 at 17:01
  • @AndreasWenzel a comment each line, done – Example person Jan 22 '22 at 17:13
  • That is better, however, I still believe that OP would have learnt more if they had written at least some of the code themself. I generally only provide a full solution once OP has provided a significant amount of code. See the link that I posted above for the exact community guidelines (they are not rules, only guidelines). – Andreas Wenzel Jan 22 '22 at 17:18
  • @AndreasWenzel I agree, I should have included more of the code that I tried and wasn't necessarily looking for an implemented solution. I tried to tokenize the string using strtok() and then recombine the str arrays back into a single string. My code unfortunately wasn't even working at that point and I was mostly looking for an approach. – Mitchell Carroll Jan 22 '22 at 17:26
  • I have found how to remove the trailing slashes, but it seems you need to group same alphabet starting characters in same string in array... very hard. I'm still trying to do that here... – Example person Jan 22 '22 at 17:41
  • Edited solution with trailing slash removal – Example person Jan 22 '22 at 18:02
  • It's looking pretty good! I'm not sure how it's working out for you, but I added a nosplit_char ++; after you memcpy in order to go from "a" to "b". Are you handling it some other way? – Mitchell Carroll Jan 22 '22 at 18:52
  • @ExamplePerson I also have an issue when there are no words starting with a particular letter. I could try to get the code to verify that the first occurrence should be the nosplit_char. – Mitchell Carroll Jan 22 '22 at 18:59
  • *nosplit_char ++; after you memcpy in order to go from "a" to "b".*, I was doing the same here, but I didn't bother to edit that in. Well, I guess that you've understood it... – Example person Jan 22 '22 at 19:01
  • *I also have an issue when there are no words starting with a particular letter. I could try to get the code to verify that the first occurrence should be the nosplit_char*, you want to verify that the first word starts with 'a'? – Example person Jan 22 '22 at 19:04
  • @MitchellCarroll: Actually, your idea of using `strtok` wasn't bad. I have added a second solution to my answer which uses that function. – Andreas Wenzel Jan 22 '22 at 22:21
1

Yes, the approach that you specify in your question seems good, in principle. However, I see the following problem:

Using strcpy will require a null-terminated source string. This means if you want to use strcpy, you will have to overwrite the / with a null character. If you don't want to have to modify the source string by writing null characters into it, then an alternative would be to use the function memcpy instead of strcpy. That way, you can specify the exact number of characters to copy and you don't require the source string to have a null terminating character. However, this also means that you will somehow have to count the number of characters to copy.

On the other hand, instead of using strcpy or memcpy, you could simply copy one character at a time from str into arr[0], until you encounter the next letter, and then copy one character at a time from str into arr[1], and so on. That solution may be simpler.

In accordance with the community guidelines for homework questions, I will not provide a full solution to your problem at this time.

EDIT: Since another answer has already provides a full solution which uses memcpy, I will now also provide a full solution, which uses the simpler solution mentioned above of copying one character at a time:

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

#define NUM_LETTERS 26
#define MAX_CHARS_PER_LETTER 99

int main( void )
{
    //declare the input string
    char *str =
        "a/apple/arm/basket/bread/car/camp/element/"
        "frog/glass/saddle/ship/water";

    //declare array which holds all the data
    //we must add 1 for the terminating null character
    char arr[NUM_LETTERS][MAX_CHARS_PER_LETTER+1];

    //this variable will store the current letter that we
    //have reached
    char curr_letter = 'a';

    //this variable will store the number of chars that are
    //already used in the current letter, which will be a
    //number between 0 and MAX_CHARS_PER_LETTER
    int chars_used = 0;

    //this variable stores whether the next character is
    //the start of a new word
    bool new_word = true;

    //initialize the arrays to contain empty strings
    for ( int i = 0; i < NUM_LETTERS; i++ )
        arr[i][0] = '\0';

    //read one character at a time
    for ( const char *p = str; *p != '\0'; p++ )
    {
        //determine whether we have reached the end of a word
        if ( *p == '/' )
        {
            new_word = true;
        }
        else
        {
            //determine whether we have reached a new letter
            if ( new_word && *p != curr_letter )
            {
                //write terminating null character to string of
                //previous letter, overwriting the "/"
                if ( chars_used != 0 )
                    arr[curr_letter-'a'][chars_used-1] = '\0';

                curr_letter = *p;
                chars_used = 0;
            }

            new_word = false;
        }

        //verify that buffer is large enough
        if ( chars_used == MAX_CHARS_PER_LETTER )
        {
            fprintf( stderr, "buffer overflow!\n" );
            exit( EXIT_FAILURE );
        }

        //copy the character
        arr[curr_letter-'a'][chars_used++] = *p;
    }

    //the following code assumes that the string pointed to
    //by "str" will not end with a "/"

    //write terminating null character to string
    arr[curr_letter-'a'][chars_used] = '\0';

    //print the result
    for ( int i = 0; i < NUM_LETTERS; i++ )
        printf( "%c: %s\n", 'a' + i, arr[i] );
}

This program has the following output:

a: a/apple/arm
b: basket/bread
c: car/camp
d: 
e: element
f: frog
g: glass
h: 
i: 
j: 
k: 
l: 
m: 
n: 
o: 
p: 
q: 
r: 
s: saddle/ship
t: 
u: 
v: 
w: water
x: 
y: 
z: 

Here is another solution which uses strtok:

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

#define NUM_LETTERS 26
#define MAX_CHARS_PER_LETTER 99

int main( void )
{
    //declare the input string
    char str[] =
        "a/apple/arm/basket/bread/car/camp/element/"
        "frog/glass/saddle/ship/water";

    //declare array which holds all the data
    //we must add 1 for the terminating null character
    char arr[NUM_LETTERS][MAX_CHARS_PER_LETTER+1];

    //this variable will store the current letter that we
    //have reached
    char curr_letter = 'a';

    //this variable will store the number of chars that are
    //already used in the current letter, which will be a
    //number between 0 and MAX_CHARS_PER_LETTER
    int chars_used = 0;

    //initialize the arrays to contain empty strings
    for ( int i = 0; i < NUM_LETTERS; i++ )
        arr[i][0] = '\0';

    //find first token
    char *p = strtok( str, "/" );

    //read one token at a time
    while ( p != NULL )
    {
        int len;

        //determine whether we have reached a new letter
        if ( p[0] != curr_letter )
        {
            curr_letter = p[0];
            chars_used = 0;
        }

        //count length of string
        len = strlen( p );
            
        //verify that buffer is large enough to copy string
        if ( chars_used + len >= MAX_CHARS_PER_LETTER )
        {
            fprintf( stderr, "buffer overflow!\n" );
            exit( EXIT_FAILURE );
        }

        //add "/" if necessary
        if ( chars_used != 0 )
        {
            arr[curr_letter-'a'][chars_used++] =  '/';
            arr[curr_letter-'a'][chars_used]   = '\0';
        }

        //copy the word
        strcpy( arr[curr_letter-'a']+chars_used, p );

        //update number of characters used in buffer
        chars_used += len;

        //find next token
        p = strtok( NULL, "/" );
    }

    //print the result
    for ( int i = 0; i < NUM_LETTERS; i++ )
        printf( "%c: %s\n", 'a' + i, arr[i] );
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39