qq52184962
qq52184962

Reputation: 49

Scan a sentence with space into *char array in C

I'm not good at using C language. Here is my dumb question. Now I am trying to get input from users, which may have spaces. And what I need to do is to split this sentence using space as delimiter and then put each fragment into char* array.
Ex:
Assuming I have char* result[10];, and the input is: Good morning John. The output should be result[0]="Good"; result[1]="morning"; result[2]="John";
I have already tried scanf("%[^\n]",input); and gets(input); Yet it is still hard to deal with String in C. And also I have tried strtok, but it seems that it only replaced the space by NULL. Hence the result will be GoodNULLmorningNULLJohn. Obviously it's not what I want. Please help my dumb question. Thanks.
Edit:
This is what I don't understand when using strtok. Here is a test code. The substr still displayed Hello there. It seems subtok only replace a null at the space position. Thus, I can't use the substr in an if statement.

int main()
{
    int i=0;
    char* substr;
    char str[] = "Hello there";
    substr = strtok(str," ");

    if(substr=="Hello"){
        printf("YES!!!!!!!!!!");
    }

    printf("%s\n",substr);

    for(i=0;i<11;i++){
        printf("%c", substr[i]);
    }

    printf("\n");
    system("pause");
    return 0; 
}

Upvotes: 0

Views: 2546

Answers (3)

Vinz
Vinz

Reputation: 3198

As most of the other answers haven't covered another thing you were asking:

strtok will not allocate temporary memory and will use your given string to replace every separator with a zero termination. This is why Good morning John becomes GoodNULLmorningNULLJohn. If it wouldn't do this, each token would print the whole rest of the string on its tail like:

result[0] = Good morning John
result[1] = morning John
result[2] = John

So if you want to keep your original input and an array of char* per word, you need 2 buffers. There is no other way around that. You also need the token buffer to stay in scope as long as you use the result array of char* pointers, else that one points to invalid memory and will cause undefined behavior.

So this would be a possible solution:

int main()
{
    const unsigned int resultLength = 10;
    char* result[resultLength];
    memset(result, 0, sizeof result);   // we should also zero the result array to avoid access violations later on

    // Read the input from the console
    char input[256];
    fgets(input, sizeof input, stdin);

    // Get rid of the newline char
    input[strlen(input) - 1] = 0;

    // Copy the input string to another buffer for your tokens to work as expected
    char tokenBuffer[256];
    strcpy(tokenBuffer, input);

    // Setting of the pointers per word
    char* token = strtok(tokenBuffer, " ");
    for (unsigned int i = 0; token != NULL && i < resultLength; i++)
    {
        result[i] = token;
        token = strtok(NULL, " ");
    }

    // Print the result
    for (unsigned int i = 0; i < resultLength; i++)
    {
        printf("result[%d] = %s\n", i, result[i] != NULL ? result[i] : "NULL");
    }
    printf("The input is: %s\n", input);

    return 0;
}

It prints:

result[0] = Good
result[1] = morning
result[2] = John
result[3] = NULL
result[4] = NULL
result[5] = NULL
result[6] = NULL
result[7] = NULL
result[8] = NULL
result[9] = NULL
The input is: Good morning John

Upvotes: 0

David Ranieri
David Ranieri

Reputation: 41026

Never use gets, is deprecated in C99 and removed from C11.

IMO, scanf is not a good function to use when you don't know the number of elements before-hand, I suggest fgets:

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

int main(void)
{
    char str[128];
    char *ptr;

    fgets(str, sizeof str, stdin);
    /* Remove trailing newline */
    ptr = strchr(str, '\n');
    if (ptr != NULL) {
        *ptr = '\0';
    }
    /* Tokens */
    ptr = strtok(str, " ");
    while (ptr != NULL) {
        printf("%s\n", ptr);
        ptr = strtok(NULL, " ");
    }
    return 0;
}

Upvotes: 4

fluter
fluter

Reputation: 13816

gets is not recommended to use, as there is no way to tell the size of the buffer. fgets is ok here because it will stop reading when the 1st new line is encountered. You could use strtok to store all the splited words in to an array of strings, for example:

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

int main(void) {
    char s[256];
    char *result[10];

    fgets(s, sizeof(s), stdin);
    char *p = strtok(s, " \n");
    int cnt = 0;
    while (cnt < (sizeof result / sizeof result[0]) && p) {
        result[cnt++] = p;
        p = strtok(NULL, " \n");
    }
    for (int i = 0; i < cnt; i++)
        printf("%s\n", result[i]);
    return 0;
}

Upvotes: 1

Related Questions