I need to make a function that gets input from the user and makes sure that it is a whole number and does not contain any character.
I wrote this code which works perfectly for integers and for single characters. But it terminates if I enter dfd i.e. multiple character input. Below is my code compiled with gcc on Linux:
#include <ctype.h>
int getint()
{
int input = 0;
int a;
int b, i = 0;
while((a = getchar()) != '\n')
{
if (a<'0'||a>'9')
{
printf("\nError in input!Please try entering a whole number again:");
input=0;
fflush(stdin);
return getint();
}
b = a - '0';
input = ((input*10) + b);
i++;
}
return input;
}
Calling fflush on an input stream invokes undefined behaviour. Even if your implementation defines it for input streams, it's not portable. There is no standard way to flush an input stream. Therefore, the fflush(stdin); is not correct. You should read the characters and discard them till and including the newline in the stdin buffer. I suggest the following change to your function.
int getint(void) {
int input = 0;
int a;
while((a = getchar()) != '\n') {
if (a < '0' || a > '9') {
printf("Error in input!Please try entering a whole number again:\n");
input = 0;
// read and discard characters in the stdin buffer up till
// and including the newline
while((a = getchar()) != '\n'); // the null statement
return getint(); // recursive call
}
input = (input * 10) + (a - '0');
}
return input;
}
Also, please read this C FAQ - If fflush won't work, what can I use to flush input?
The problem may be that calling fflush(stdin) is undefined. fflush is for flushing an output stream, not an input stream. Try replacing it with another way to clear the remaining input buffer, like while (getchar() != '\n'); and see if that resolves the issue. (you should probably do something more robust like also catching EOF so you're not in an infinite loop)
Changing the fflush to fpurge caused your program to start working for me.
Related
This question already has an answer here:
Canonical vs. non-canonical terminal input
(1 answer)
Closed 4 years ago.
I'm trying to write a simple program that asks a user to choose from a menu in a loop.
I use getchar() to get the input, however i've noticed that when I enter a char and press 'Enter' the program makes two loops (as if i pressed twice) one the char as an input and another for 'Enter' as an input.
How do I fix this?
getchar() returns the first character in the input buffer, and removes it from the input buffer. But other characters are still in the input buffer (\n in your example). You need to clear the input buffer before calling getchar() again:
void clearInputBuffer() // works only if the input buffer is not empty
{
do
{
c = getchar();
} while (c != '\n' && c != EOF);
}
The easiest way is to filter out the enter key as the return value from getchar
char c = (char)getchar();
if ( c != '\n' ) {
...
}
Add a getchar() after the getchar() :P
How about
#include <stdio.h>
/*! getline() reads one line from standard input and copies it to line array
* (but no more than max chars).
* It does not place the terminating \n in line array.
* Returns line length, or 0 for empty line, or EOF for end-of-file.
*/
int getline(char line[], int max)
{
int nch = 0;
int c;
max = max - 1; /* leave room for '\0' */
while ((c = getchar()) != EOF) {
if (c == '\n')
break;
if (nch < max) {
line[nch] = c;
nch = nch + 1;
}
}
if (c == EOF && nch == 0)
return EOF;
line[nch] = '\0';
return nch;
}
Source
You've kind of answered your own question; you have to deal with the newline character somehow.
There are several options. If your menu options are numbered, you can use scanf() to read in an integer value and switch based on that:
printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
case 0 : do_something(); break;
case 1 : do_something_else(); break;
...
default: bad_option(); break;
}
The advantage of this option is that the %d conversion specifier skips over any leading whitespace, including newline characters, so you don't have to worry about any unread \n clogging up the input stream (in fact, most of the conversion specifiers skip leading whitespace; %c doesn't, making it behave a lot like getchar()).
The disadvantage of this option is that if someone fat-fingers a non-digit character in their input, it won't be read with the %d conversion specifier, and will stay stuck in the input stream until a call to getchar() or scanf() with a %s or %c conversion specifier.
A better option is to read all input as character strings using fgets(), then parse and validate as necessary.
/**
* Prints a prompt to stdout and reads an input response, writing
* the input value to option.
*
* #param prompt [in] - prompt written to stdout
* #param option [out] - option entered by user
*
* #return - 1 on success, 0 on failure. If return value is 0, then option
* is not changed.
*/
int getOption(const char *prompt, char *option)
{
char input[3]; // option char + newline + 0 terminator
int result = 0;
printf("%s: ", prompt);
fflush(stdout);
if (fgets(input, sizeof input, stdin))
{
/**
* Search for a newline character in the input buffer; if it's not
* present, then the user entered more characters than the input buffer
* can store. Reject the input, and continue to read from stdin until
* we see a newline character; that way we don't leave junk in the
* input stream to mess up a future read.
*/
char *newline = strchr(input, '\n');
if (!newline)
{
printf("Input string is too long and will be rejected\n");
/**
* Continue reading from stdin until we find the newline
* character
*/
while (!newline && fgets(input, sizeof input, stdin))
newline = strchr(input, '\n');
}
else
{
*option = input[0];
result = 1;
}
}
else
printf("Received error or EOF on read\n");
return result;
}
Yes, that's a lot of work to read in one stupid menu option, and that's the simple version. Welcome to the wonderful world of interactive input processing in C.
Not sure why the following code does not return the number of spaces when providing a sentence for getchar(). I want the program to return one line of text that records the number of spaces in a given sentence.
#include <stdio.h>
main()
{
int blarp, space;
space = 0;
printf("give me input\n");
while((blarp = getchar()) != EOF)
{
if(blarp == ' ')
{
space++;
}
}
printf("there are %d spaces", space);
}
As OP wants to operate on a line of data "...to return one line of text ...", detect an end-of-line '\n' as well as EOF.
while((blarp = getchar()) != EOF)
while((blarp = getchar()) != EOF && blarp != '\n')
The problem is with the EOF you used in the stopping condition of while it means End Of File and is used when reading from a file. As notified by Pooya it can work by giving EOF as input from the keyboard as:
Actually you can send EOF as input. if you are working with Windows hit Ctrl+Z and in Linux Ctrl+D. It is treated as EOF – Pooya
But, as notified by William Pursell, that would be a workaround rather than a valid input:
Pooya, no, you can't "send EOF". You can close the input stream, which will cause getchar to return EOF. ctrl-D just closes the input stream. Perhaps this is semantics, but "send EOF" implies a sentinel character, which does not exist. – William Pursell
But, to avoid the complication, you will have to use some sentinel value to stop the while loop, I have used '!' here but you can use any suitable character:
#include <stdio.h>
main()
{
int blarp, space;
space = 0;
printf("give me input\n");
while((blarp = getchar()) != '!')
{
if(blarp == ' ')
{
space++;
}
}
printf("there are %d spaces\n", space);
}
You will have to enter '!', the sentinel value, at the end of your input to stop the while loop.
Here is the snapshot of my output:
This is my very first post on Stack Overflow, so I hope I don't step on anyone's toes.
Of course, all inputs are welcome and appreciated, but those most suited to answer would have actually read the book, C Programming Language, 2nd ed.
I have just finished coding Exercise 6-4, but I cannot seem to figure something out. Why does the getword() function not read EOF until I press Ctrl+D (I code in C in an Arch Linux VM)?
Many of my previous exercises from the book require reading from stdin. One way I would do it is via something like
while ((c = getchar()) != EOF) {...}
In such an instance, I never have to press Ctrl+D. I enter in my input, press Enter, the stdin buffer gets flushed out, and EOF is detected automatically. The getword() function also relies on getchar() at its base, so why does it hang my program?
The getword() function is called from main():
while (getword(word, MAX_WORD) != EOF) {
if (isalpha(word[0])) {
root = addtree(root, word);
}
}
The getword() function itself:
int getword(char *word, int lim) {
char *w = word;
int c;
while (isspace(c = getch())) {
}
if (c != EOF) {
*w++ = c;
}
// This point is reached
if (!isalpha(c)) {
// This point is never reached before Ctrl+D
*w = '\0';
return c;
}
for ( ; --lim > 0; w++) {
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
}
*w = '\0';
return word[0];
}
I put comments to indicate the point where I determined that EOF is not being read.
The getch() and ungetch() functions are the same ones used in the Polish notation calculator from Chapter 4 (and that program was able to read EOF automatically - by pressing Enter):
#define BUF_SIZE 100
char buf[BUF_SIZE];
int bufp = 0;
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) {
if (bufp >= BUF_SIZE) {
printf("ungetch: too many characters\n");
}
else {
buf[bufp++] = c;
}
}
Thus far, this is the first program I wrote since the beginning of this book that requires me to manually enter the EOF via Ctrl+D. I just can't seem to figure out why.
Much appreciation in advance for explanations...
Having to type Ctrl+D to get EOF is the normal behavior for Unix-like systems.
For your code snippet:
while ((c = getchar()) != EOF) {...}
pressing Enter definitely shouldn't terminate the loop (unless your tty settings are badly messed up).
Try compiling and running this program:
#include <stdio.h>
int main( void )
{
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
return 0;
}
It should print everything you type, and it should terminate only when you type control-D at the beginning of a line (or when you kill it with control-C).
The 'not reached' point would only be reached if you did something like type a punctuation mark in the input - or you read EOF. If you type a letter, or spaces, then it is bypassed.
When input is coming from a terminal (standard input), then EOF is not detected until you type Control-D (or whatever is specified in the stty -a output) after you enter a newline, or after you hit another Control-D (so two in a row). The code reads through newlines because the newline character '\n' satisfies isspace().
The source of my confusion vis-a-vis my previous programs was that the effect of my previous programs were always printed to stdout inside the while loop, so I always immediately saw the result without needing to feed in EOF. For this one, the tree is not printed until after the while loop ends, so the EOF encounter was needed. I failed to recognize that, and that's why I was going insane.
Thanks again for setting me straight!
This question already has an answer here:
Canonical vs. non-canonical terminal input
(1 answer)
Closed 4 years ago.
I'm trying to write a simple program that asks a user to choose from a menu in a loop.
I use getchar() to get the input, however i've noticed that when I enter a char and press 'Enter' the program makes two loops (as if i pressed twice) one the char as an input and another for 'Enter' as an input.
How do I fix this?
getchar() returns the first character in the input buffer, and removes it from the input buffer. But other characters are still in the input buffer (\n in your example). You need to clear the input buffer before calling getchar() again:
void clearInputBuffer() // works only if the input buffer is not empty
{
do
{
c = getchar();
} while (c != '\n' && c != EOF);
}
The easiest way is to filter out the enter key as the return value from getchar
char c = (char)getchar();
if ( c != '\n' ) {
...
}
Add a getchar() after the getchar() :P
How about
#include <stdio.h>
/*! getline() reads one line from standard input and copies it to line array
* (but no more than max chars).
* It does not place the terminating \n in line array.
* Returns line length, or 0 for empty line, or EOF for end-of-file.
*/
int getline(char line[], int max)
{
int nch = 0;
int c;
max = max - 1; /* leave room for '\0' */
while ((c = getchar()) != EOF) {
if (c == '\n')
break;
if (nch < max) {
line[nch] = c;
nch = nch + 1;
}
}
if (c == EOF && nch == 0)
return EOF;
line[nch] = '\0';
return nch;
}
Source
You've kind of answered your own question; you have to deal with the newline character somehow.
There are several options. If your menu options are numbered, you can use scanf() to read in an integer value and switch based on that:
printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
case 0 : do_something(); break;
case 1 : do_something_else(); break;
...
default: bad_option(); break;
}
The advantage of this option is that the %d conversion specifier skips over any leading whitespace, including newline characters, so you don't have to worry about any unread \n clogging up the input stream (in fact, most of the conversion specifiers skip leading whitespace; %c doesn't, making it behave a lot like getchar()).
The disadvantage of this option is that if someone fat-fingers a non-digit character in their input, it won't be read with the %d conversion specifier, and will stay stuck in the input stream until a call to getchar() or scanf() with a %s or %c conversion specifier.
A better option is to read all input as character strings using fgets(), then parse and validate as necessary.
/**
* Prints a prompt to stdout and reads an input response, writing
* the input value to option.
*
* #param prompt [in] - prompt written to stdout
* #param option [out] - option entered by user
*
* #return - 1 on success, 0 on failure. If return value is 0, then option
* is not changed.
*/
int getOption(const char *prompt, char *option)
{
char input[3]; // option char + newline + 0 terminator
int result = 0;
printf("%s: ", prompt);
fflush(stdout);
if (fgets(input, sizeof input, stdin))
{
/**
* Search for a newline character in the input buffer; if it's not
* present, then the user entered more characters than the input buffer
* can store. Reject the input, and continue to read from stdin until
* we see a newline character; that way we don't leave junk in the
* input stream to mess up a future read.
*/
char *newline = strchr(input, '\n');
if (!newline)
{
printf("Input string is too long and will be rejected\n");
/**
* Continue reading from stdin until we find the newline
* character
*/
while (!newline && fgets(input, sizeof input, stdin))
newline = strchr(input, '\n');
}
else
{
*option = input[0];
result = 1;
}
}
else
printf("Received error or EOF on read\n");
return result;
}
Yes, that's a lot of work to read in one stupid menu option, and that's the simple version. Welcome to the wonderful world of interactive input processing in C.
gcc 4.4.2
I was reading an article about scanf. I personally have never checked the return code of a scanf.
#include <stdio.h>
int main(void)
{
char buf[64];
if(1 == scanf("%63s", buf))
{
printf("Hello %s\n", buf);
}
else
{
fprintf(stderr, "Input error.\n");
}
return 0;
}
I am just wondering what other techniques experienced programmers do when they use scanf when they want to get user input? Or do they use another function or write their own?
Thanks for any suggestions,
EDIT =========
#include <stdio.h>
int main(void)
{
char input_buf[64] = {0};
char data[64] = {0};
printf("Enter something: ");
while( fgets(input_buf, sizeof(input_buf), stdin) == NULL )
{
/* parse the input entered */
sscanf(input_buf, "%s", data);
}
printf("Input [ %s ]\n", data);
return 0;
}
I think most programmers agree that scanf is bad, and most agree to use fgets and sscanf. However, I can use fgets to readin the input. However, if I don't know what the user will enter how do I know what to parse. For example, like if the user was to enter their address which would contain numbers and characters and in any order?
Don't use scanf directly. It's surprisingly hard to use. It's better to read an entire line of input and to then parse it (possibly with sscanf).
Read this entry (and the entries it references) from the comp.lang.c FAQ:
http://c-faq.com/stdio/scanfprobs.html
Edit:
Okay, to address your additional question from your own edit: If you allow unstructured input, then you're going to have to attempt to parse the string in multiple ways until you find one that works. If you can't find a valid match, then you should reject the input and prompt the user again, probably explaining what format you want the input to be in.
For anything more complicated, you'd probably be better off using a regular expression library or even using dedicated lexer/parser toolkits (e.g. flex and bison).
I don't use scanf() for interactive user input; I read everything as text using fgets(), then parse the input as necessary, using strtol() and strtod() to convert text to numeric values.
One example of where scanf() falls down is when the user enters a bad numeric value, but the initial part of it is valid, something like the following:
if (scanf("%d", &num) == 1)
{
// process num
}
else
{
// handle error
}
If the user types in "12e4", scanf() will successfully convert and assign the "12" to num, leaving "e4" in the input stream to foul up a future read. The entire input should be treated as bogus, but scanf() can't catch that kind of error. OTOH, if I do something like:
if (fgets(buffer, sizeof buffer, stdin))
{
int val;
char *chk;
val = (int) strtol(buffer, &chk, 10);
if (!isspace(*chk) && *chk != 0)
{
// non-numeric character in input; reject it completely
}
else
{
// process val
}
}
I can catch the error in the input and reject it before using any part of it. This also does a better job of not leaving garbage in the input stream.
scanf() is a great tool if you can guarantee your input is always well-formed.
scanf() has problems, in that if a user is expected to type an integer, and types a string instead, often the program bombs. This can be overcome by reading all input as a string (use getchar()), and then converting the string to the correct data type.
/* example one, to read a word at a time */
#include <stdio.h>
#include <ctype.h>
#define MAXBUFFERSIZE 80
void cleartoendofline( void ); /* ANSI function prototype */
void cleartoendofline( void )
{
char ch;
ch = getchar();
while( ch != '\n' )
ch = getchar();
}
main()
{
char ch; /* handles user input */
char buffer[MAXBUFFERSIZE]; /* sufficient to handle one line */
int char_count; /* number of characters read for this line */
int exit_flag = 0;
int valid_choice;
while( exit_flag == 0 ) {
printf("Enter a line of text (<80 chars)\n");
ch = getchar();
char_count = 0;
while( (ch != '\n') && (char_count < MAXBUFFERSIZE)) {
buffer[char_count++] = ch;
ch = getchar();
}
buffer[char_count] = 0x00; /* null terminate buffer */
printf("\nThe line you entered was:\n");
printf("%s\n", buffer);
valid_choice = 0;
while( valid_choice == 0 ) {
printf("Continue (Y/N)?\n");
scanf(" %c", &ch );
ch = toupper( ch );
if((ch == 'Y') || (ch == 'N') )
valid_choice = 1;
else
printf("\007Error: Invalid choice\n");
cleartoendofline();
}
if( ch == 'N' ) exit_flag = 1;
}
}
I make a loop call fgets until the end of the line is read, and then call sscanf to parse the data. It's a good idea to check whether sscanf reaches the end of the input line.
I rarely use scanf. Most of the times, I use fgets() to read data as a string. Then, depending upon the need, I may use sscanf(), or other functions such as strto* family of functions, str*chr(), etc., to get data from the string.
If I use scanf() or fgets() + sscanf(), I always check the return values of the functions to make sure they did what I wanted them to do. I also don't use strtok() to tokenize strings, because I think the interface of strtok() is broken.