hi, i have the following problem::

i want to read a string from the stdin. The string may contain white spaces so {i think} the scanf family of functions is out of the question... I think the most appropriate function for this case is fgets {if i am wrong please correct me}. Fgets is that it stops when it reads an enter in the input stream and problem arises when i use a menu prior to reading a string that causes an enter to be left in the stream... Is there anyway to circumvent this problem? The only way i found is if i call fgets 2 times in a row{so that the first will discart the enter , and the second will read the string}

here is a sample code::

#include <stdio.h>

int Menu()
{

	int choice = 0;		/* menu choice value */
	/* with the following loop we force the user to choose
	 * on of the 3 valid answers: 1, 2, or 3
	 */

	do { 
		fprintf(stdout, "Please choose one of the following:\n"
				"1. do1 \n"
				"2. do2 \n"
				"3. do3 \n");
		fscanf(stdin, "%d", &choice);

	} while ( choice != 1 && choice != 2 && choice != 3);	

	return choice;
}	


int main()
{
	int mychoice = Menu();
	printf("\n\n\tyour choice was:: %d\n", mychoice);
	printf("\nnow lets read a string with fgets\n", mychoice);
	char buffer[256];
	fgets(buffer, sizeof(buffer), stdin);
	fgets(buffer, sizeof(buffer), stdin);
	//fscanf(stdin, "%s", buffer);	//unfortunatelly it doesn't work because it stops after reading a white space
	printf("\nhere is your string ::: %s\n", buffer);
	
	return 0;
}

thanks in advance for your help,
nicolas

PS:: also another weird behaviour is when instead of a number{in the menu response} i type a letter, then i get in an infinite loop.Why is this happening?

Salem commented: Sanity, in the midst of spam :) +13

Dani AI

Generated

Short, practical summary and a safe pattern to follow.

The symptom here is classic: a formatted read (like scanf("%d", …)) consumes only the digits and leaves the leftover characters (often the newline) on stdin, so a subsequent line-oriented read returns immediately. Calling fgets twice is a quick hack but fragile. As suggested, treat stdin as a stream of lines: always read a full line and then parse it. That avoids the infinite-loop / leftover-input problem and makes validation straightforward; 's caution about unsafe string reads is also worth keeping in mind.

A robust C pattern (read a line, then parse and validate):

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

int read_int(const char *prompt)
{
    char buf[128];
    char *end;
    long val;

    for (;;) {
        fputs(prompt, stdout);
        if (!fgets(buf, sizeof buf, stdin)) return 0; /* EOF */
        errno = 0;
        val = strtol(buf, &end, 10);
        if (end == buf) { puts("please enter a number"); continue; }
        while (*end == ' ' || *end == '\t') ++end;
        if (*end != '\n' && *end != '\0') { puts("trailing input"); continue; }
        if (errno == ERANGE || val < INT_MIN || val > INT_MAX) { puts("out of range"); continue; }
        return (int)val;
    }
}

If you must mix token reads with line reads, do not use fflush(stdin) (that's undefined behavior). Instead discard the remainder of the current line:

void drain_stdin(void)
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF) { }
}

C++ tip: use std::getline then parse with std::istringstream or std::stoi (with error checks). Also remember: if the user types a line longer than your buffer, fgets will stop short and leave the rest (including newline) for the next read — account for that when validating. Following the line-then-parse approach makes input handling predictable and easy to validate.

Recommended Answers

All 8 Replies

Use fgets() for everything.

So for example.

int Menu()
{
	char buff[BUFSIZ];
	int choice = 0;		/* menu choice value */
	/* with the following loop we force the user to choose
	 * on of the 3 valid answers: 1, 2, or 3
	 */

	do { 
		fprintf(stdout, "Please choose one of the following:\n"
				"1. do1 \n"
				"2. do2 \n"
				"3. do3 \n");
		fgets( buff, sizeof buff, stdin );
		if ( sscanf( buff, "%d", &choice ) != 1 ) {
			printf( "I asked for a number!\n" );
		}
	} while ( choice != 1 && choice != 2 && choice != 3);	

	return choice;
}

This also fixes your "what if they type the wrong thing" problem as well.

commented: thanks! +2

thanks salem, it works! I understand why your code solves the problem "type the wrong thing"...

The thing i can't understand is why scanf can't work together with fgets...i think i 've read somewhere that it isn't the best thing to mix io functions from different families but i can't understand why...

>>it isn't the best thing to mix io functions from different families
but scanf() and fgets() are of the same family of functions, all declared in stdio.h But it is NOT good to use scanf() for string input because it can (potentially) corrupt and possibly crash your program if you enter a string that is longer then the buffer size you want to hold it.

scanf() uses the bare minimum of characters necessary to perform the conversion, and leaves the first character which cannot be converted on the input stream.

So if you type in
42\n
and scan that with "%d", then 42 will be used and \n will be left.

Now you come along with fgets(), and boom, first character is \n, and it's "thank you and good night from fgets()".

commented: "...and boom". I like your special effects +4

thank you all for your help... :)

i read the tutorials on input.Excellent work from Dave Sinkula.

It seems that my biggest mistake concerning fgets was that i didn't know this::
When the incoming text is one less than the size of the buffer, the newline is retained.

PS:: are there any simular tutorials for c++?{ i found the code snipset section but i don't know how can i search it...}

thanks again for all your help,
nicolas

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.