Enterprevic
Enterprevic

Reputation: 69

Splitting an array without an inbuilt function

The code below is meant to take an input string like My name is Smith and outputs

My 
name 
is 
Smith

and also has to exclude things like: , . and space, just these three, but instead it outputs, I'm not allowed to use any thing like strlen or strtok

My
y

name
ame 
me
e

is
s

Smith
mith
ith
th
h

I've searched for the error everywhere in the code and I can't seem to figure it out

int main()
{
    int wordSize = 0;
    char str[81];
    char* ptr_to_word[81];

    gets_s(str);

    for (char* res_p = &(str[0]); *res_p != '\0'; res_p++) {
        if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' '))
        {
            ptr_to_word[wordSize] = res_p;
            wordSize++;
        }
    }

    if (wordSize == 0)
    {
        printf("no solution");
    }

    else
    {
        for (int i = 0; i < wordSize; i)
        {
            char* a = ptr_to_word[i];
            while ((*a != '.') && (*a != ',') && (*a != ' ') && (*a != '\0'))
            {
                printf("%c", *a);
                a++;
            }
            printf("\n");
        }
    }
    return 0;
}

Upvotes: 1

Views: 86

Answers (2)

Yulia
Yulia

Reputation: 165

First of all, this condition

if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' ')

will always be true, think about it, *res_p is always not '.' OR not ','. However, if you change the operator to && your code will still produce the same result.

The first loop with || operator writes a pointer to every character in your input string to the array ptr_to_word, and ' 's and '.'s are not printed only because of a condition in the second loop.

If you change the operator to && like so,

if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' ')

you will write all the pointers, except for the '.', ',' and ' ', into the array, e.g. ptr_to_word[0] will point to 'My name is Smith', ptr_to_word[1] to 'y name is Smith', etc.

I have made a few changes to your code so that it would work as intended.

void split_str(char *str)
{
    int wordSize = 0;
    char *ptr_to_word[81];
    char *res_p = &(str[0]);
    int i = 0;

    for (; *res_p != '\0'; res_p++) {

        if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' '))
        {
            wordSize++;
        }
        else
        {
            ptr_to_word[i] = res_p - wordSize;
            wordSize = 0;
            i++;
        }
    }

    ptr_to_word[i] = res_p - wordSize;
    ptr_to_word[i + 1] = NULL;

    if (i == 0)
    {
        printf("no solution\n");
    }
    else
    {
        for (int i = 0; ptr_to_word[i]; i++)
        {
            char* a = ptr_to_word[i];
            while ((*a != '.') && (*a != ',') && (*a != ' ') && (*a != '\0'))
            {
                printf("%c", *a);
                a++;
            }
            printf("\n");
        }
    }
}
int main() {
    split_str("My name is Smith\n");
}

This way you are writing a new value to the pointer array only when dot, comma or a space is encountered. For all other chars, you just do wordSize++ to keep track of the length of the word. I used pointer arithmetic to move back to the start of the word (ptr_to_word[i] = res_p - wordSize;).

I have tried to make only minimal changes to your code, just enough to make it work. There are still things I would change to make it better, e.g. the first loop finishes when the '\0' is encountered. Most likely, the last word finishes with a '\0', not a '.' or a ' '. Thus, it won't be written to the ptr_to_word, and I had to manually write it after the loop. Also, you don't even need two loops, you can use the same pointer. arithmetic to output the word as soon as it is found. So I'd advise you to do. a self-code-review, and optimize it further.

Good luck!

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 311126

In the condition of the if statement within this loop

for (char* res_p = &(str[0]); *res_p != '\0'; res_p++) {
    if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' '))
    {
        ptr_to_word[wordSize] = res_p;
        wordSize++;
    }
}

you have to use the logical AND operator instead of the logical OR operator like

    if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' '))

Nevertheless the loop in any case is wrong because the variable wordSize does not count words but counts each character that is not equal to the characters listed in the if statement.

To output separate words there is no need to declare an array of pointers to starts of words. You can output each word as soon as its start is found.

Here is a very simple demonstrative program.

#include <stdio.h>

int main(void) 
{
    enum { N = 81 };
    char s[N];

    fgets( s, N, stdin );

    for ( const char *p = s; *p; )
    {
        int wordSize = 0;
        while ( *p && *p != '.' && *p != ',' && *p != ' ' && *p != '\t' && *p != '\n' )
        {
            ++wordSize;
            ++p;
        }

        if ( wordSize )
        {
            printf( "%.*s\n", wordSize, p - wordSize );
        }
        else
        {
            ++p;
        }
    }

    return 0;
}

If to input "My name is Smith" then the program output might look like

My
name
is
Smith

I appended the list of delimiters with the tab character '\t' and the new line character '\n' that can be inserted in the input string by the function fgets.

Upvotes: 2

Related Questions