Saad Hashmi
Saad Hashmi

Reputation: 29

count the occurrences of each word in that phrase using C language

I am not understanding why, when I am doing sub=strtok(NULL,delim);, instead of going to the next word, it is making it null. Please try this code and help me resolve the problem.

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

int main()
{
    char str[100],str1[100],*sub,*sub1;
    int c1;
    printf("\nEnter the string or sentence: ");
    scanf("%[^\n]%*c",str);
    strcpy(str1,str);
    char delim[]=" ";
    sub=strtok(str,delim);
    while(sub !=NULL)
    {
        c1=0;
        sub1=strtok(str1,delim);
        while(sub1 !=NULL)
        {
          if(!strcmp(sub,sub1))
          {
             c1++;
          }
          sub1=strtok(NULL,delim);
        }
        printf("\n%s : %d",sub,c1);
        sub=strtok(NULL,delim);
    }
    return 0;
}

Upvotes: 1

Views: 363

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598309

You can't parse multiple string buffers concurrently with strtok(), like you are trying to do. Internally, strtok() maintains a static reference to the input string, that is how it knows which string to advance through when subsequent calls specify a NULL string pointer.

Before entering your outer loop, you call strtok(str) to set the internal reference to str. Then, inside the loop, you call strtok(str1) to reset that reference to str1. So, on the 1st iteration of your outer loop, before entering the inner loop, strtok() has already lost its reference to your str buffer since you replace it with the str1 buffer. Then, after the inner loop has finished scanning str1, your outer loop breaks when calling sub=strtok(NULL,delim); because there are no more tokens to read from str1.

For what you are attempting, use strtok_r() instead:

The strtok_r() function is a reentrant version strtok(). The saveptr argument is a pointer to a char * variable that is used internally by strtok_r() in order to maintain context between successive calls that parse the same string.

...

Different strings may be parsed concurrently using sequences of calls to strtok_r() that specify different saveptr arguments.

Also, because strtok(_r)() is destructive to the input string, inserting '\0' after each token found, you need to reset the contents of your 2nd string buffer back to the original string before entering the inner loop. Otherwise, the inner loop will stop scanning after is reads the 1st word in your 2nd string buffer.

Try something more like this:

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

int main()
{
    char orig[100], str1[100], str2[100], *sub1, *sub2, *save1, *save2;
    char delim[] = " ";
    int c1;

    printf("\nEnter the string or sentence: ");
    scanf("%[^\n]%*c", orig);

    strcpy(str1, orig);
    sub1 = strtok_r(str1, delim, &save1);
    while (sub1 != NULL)
    {
        c1 = 0;

        strcpy(str2, orig);
        sub2 = strtok_r(str2, delim, &save2);
        while (sub2 != NULL)
        {
            if (strcmp(sub1, sub2) == 0)
            {
                ++c1;
            }
            sub2 = strtok_r(NULL, delim, &save2);
        }

        printf("\n%s : %d", sub1, c1);

        sub1 = strtok_r(NULL, delim, &save1);
    }

    return 0;
}

Otherwise, you can use a single loop through a single string buffer, storing the word counts in a separate array, eg:

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

typedef struct _wordInfo
{
    char* str;
    int count;
} wordInfo;

int main()
{
    char str[100], *sub;
    char delim[] = " ";
    wordInfo words[100], *word;
    int numWords = 0;

    printf("\nEnter the string or sentence: ");
    scanf("%[^\n]%*c", str);

    sub = strtok(str, delim);
    while (sub != NULL)
    {
        word = NULL;
        for (int i = 0; i < numWords; ++i)
        {
            if (strcmp(sub, words[i].str) == 0)
            {
                word = &words[i];
                break;
            }
        }

        if (!word)
        {
            if (numWords == 100) break;
            word = &words[numWords++];
            word->str = sub;
            word->count = 1;
        }
        else {
            word->count++;
        }

        sub = strtok(NULL, delim);
    }

    for(int i = 0; i < numWords; ++i)
    {
        word = &words[i];
        printf("\n%s : %d", word->str, word->count);
    }

    return 0;
}

Upvotes: 1

Related Questions