user3741679
user3741679

Reputation: 98

C: The first element of a char cannot be detected

I am learning getting inputs from key board. I want the user create a or more strings from the input, each string is considered as a line, the program will not terminate until a specified char is pressed. Then store these strings to the buffer.

However, when I print out the buffer, the first few elements of the string are always missing. Here is my code:

#include<stdio.h>
int main(void){
    printf("Please type the string:\n");
    char buffer[1000];
    int c;
    while( (c = getchar()) != ' ' ) {
        fgets(buffer, sizeof(buffer), stdin);
        printf("The output string is: \n%s\n", buffer);
        if((c = getchar())== ' '){
            printf("A space is detected!\n");
            break;
        }
    }
}

The output is:

Please type the string:
abcdefg
The output string is: 
bcdefg

hijklmn
The output string is: 
jklmn

opqrst
The output string is: 
qrst


A space is detected!
Program ended with exit code: 0

Which part did I go wrong? Any hints are very much appreciated.

Upvotes: 2

Views: 585

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84561

The problem you are having is both getchar(), and fgets in your code are reading from stdin. Since you call getchar() first in your test, it was consuming the first character of your string, when you called it again, another character disappeared...

You don't need getchar() at all to end your loop. All you care about for breaking your loop as you have explained is whether the user enters a space as the first character. fgets does not skip leading whitespace, so any leading space entered by the user will be captured at the beginning of buffer. So to satisfy your loop-exit condition, all you need to do is check if the first character of buffer is a space.

How? The simple way is to just derererence buffer, e.g. *buffer returns the first character in buffer. How? In pointer notation, buffer + 0 is the offset you want in buffer, so to get the character at that location, you dereference, e.g. *(buffer + 0), which of course is just *buffer, which is the equivalent of buffer[0].

So, putting it altogether, and getting rid of getchar(), and adding strlen to properly validate that the string fit in buffer and to get the location for the trailing '\n' read and included in buffer by fgets (which leaves you with the length of trimmed string as a benefit), you could do something similar to:

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

#define MAXC 1000   /* if you need a constant, define one (or more) */

int main (void) {

    char buffer[MAXC] = "";   /* initialize strings zero (good practice) */

    for (;;) {              /* loop continually taking input */
        size_t len;         /* variable for buffer length */

        printf ("\nenter string: ");            /* prompt */

        if (!fgets (buffer, sizeof buffer, stdin))   /* read input */
            break;          /* exit if user cancels input */
        len = strlen (buffer);                  /* get length */
        if (len && buffer[len-1] == '\n')   /* check if last char is \n */
            buffer[--len] = 0;              /* overwrite with nul-char */
        else {  /* otherwise string too long */
            fputs ("error: string too long.\n", stderr);
            return 1;
        }

        if (*buffer == ' ')     /* check if 1st char of buffer is ' ' */
            break;

        printf ("buffer: %s  (%zu chars)\n", buffer, len);  /* output */
    }
}

Example Use/Output

$ ./bin/fgetsspace

enter string: my dog has fleas
buffer: my dog has fleas  (16 chars)

enter string: my cat has none
buffer: my cat has none  (15 chars)

enter string:  bye

(note: a space was entered before bye above, e.g. " bye")

Look things over and let me know if you have further questions.


Separating Words with strtok

To separate each line into individual words you can use strtok. The first argument is the buffer (for the 1st call), the second parameter is a list of characters to use as delimeters between the words (e.g. if you want to separate on space include a space, to not include the '.' at the end of a sentence include that as well -- and include the '\n'). After the 1st call to strtok all subsequent calls to get the remaining words uses NULL in place of buffer, e.g.

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

#define MAXC 1000   /* if you need a constant, define one (or more) */

int main (void) {

    char buffer[MAXC] = "";   /* initialize strings zero (good practice) */

    for (;;) {                  /* loop continually taking input */
        size_t len;             /* variable for buffer length */
        char *delim = " .\n",   /* delmiters for strtok */
            *p = buffer;        /* pointer to buffer for strtok */

        printf ("\nenter string: ");            /* prompt */

        if (!fgets (buffer, sizeof buffer, stdin))   /* read input */
            break;          /* exit if user cancels input */
        len = strlen (buffer);                  /* get length */
        if (len && buffer[len-1] == '\n')   /* check if last char is \n */
            buffer[--len] = 0;              /* overwrite with nul-char */
        else {  /* otherwise string too long */
            fputs ("error: string too long.\n", stderr);
            return 1;
        }

        if (*buffer == ' ')     /* check if 1st char of buffer is ' ' */
            break;

        printf ("buffer: %s  (%zu chars)\n", buffer, len);  /* output */

        p = strtok (buffer, delim); /* 1st call to strtok uses buffer */
        while (p != NULL) {
            printf ("  %s\n", p);
            p = strtok (NULL, delim);   /* subsequent calls use NULL */
        }
    }
}

(note: the original buffer is modified, so make a copy if you need to preserve the original)

Example Use/Output

$ ./bin/fgetsspace

enter string: my dog has fleas
buffer: my dog has fleas  (16 chars)
  my
  dog
  has
  fleas

enter string: my cat has none
buffer: my cat has none  (15 chars)
  my
  cat
  has
  none

enter string:  bye

Upvotes: 3

Alceste_
Alceste_

Reputation: 614

Answering in addition to my initial comment and the issue:

First, quoting myself:

I believe that when using getChar(), you efficiently remove the character from stdin buffer.

As stated since then by other people, the problem is that your call to getchar function consume and input, efficiently removing it from stdin buffer.

See Jim Buck's answer for detailed informations on the precise behavior of your application.

Now, what should you do ?

First, the if inside the while loop is not necessary, and using your application right now must be pretty odd. Try doing :

#include<stdio.h>
int main(void){
    printf("Please type the string:\n");
    char buffer[1000];
    int c;
    while( (c = getchar()) != ' ' ) {
        fgets(buffer, sizeof(buffer), stdin);
        printf("The output string is: \n%s\n", buffer);
    }
    printf("A space is detected!\n");
}

Instead to prevent unnecessary user inputs. Your loop is basically an infinite loop so there is no need to check at the end of every iteration if the loop should terminate, the while statement is already doing that pretty damn well. :P

Now, to prevent the input from being taken out of buffer, I would consider using the buffer's first element instead of "c" variable.

Like so :

#include<stdio.h>
int main(void){
    printf("Please type the strings:\n");
    char buffer[1000];
    while( (buffer[0] = getchar()) != ' ' ) { // Now reads directly into buffer
        fgets(buffer + 1, sizeof(buffer), stdin); // + 1 prevents overriding the char we just read. 
        printf("The output string is: \n%s\n", buffer);
    }
    printf("A space is detected!\n");
}

Have a nice day!

Upvotes: 0

Jim Buck
Jim Buck

Reputation: 20726

getchar swallows up a character. Your first iteration gets one character swallowed up by the initial call in the while, and then successive iterations get two characters swallowed up, one by the getchar you use to detect a space and then again the one in the while.

Upvotes: 0

Related Questions