Environment:
Windows 7 Enterprise SP1
gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)
I'm new to C language (not new to programming).
I'm migrating a big program to C language. All works fine except for this.
I create this small program to reproduce the problem:
int AAA ()
{
const char *p = "Enter to proceed.. X to Exit.. : ";
const char *ok = "OK" ;
char proceed1 ;
char proceed2 ;
printf(p) ;
fflush(stdin) ;
scanf("%c",&proceed1);
if (proceed1 == 'X'){
return 0 ; }
sleep(3) ;
fflush(stdin) ;
printf("\n%s",p) ;
scanf("%c",&proceed2);
if (proceed2 == 'X'){
return 0 ; }
printf("\n%s",ok) ;
return 0 ;
}
All worls fine, BUT if the user (wrongly) hits twice the enter key at the proceed1 then scanf of proceed2 automatically reads the newline character '\n' that remain in the buffer...
I tried all: rewind(stdin), fseek stdin, a getchar before the second prompt to bypass the problem, but let the scanf hang if the user correctly hit enter key once.
I repeat I'm new to C language. I understand the problem can be bypassed writing a scanf that does not accpet the \n alone.
But before proceeding I ask here this question:
How to completely clear the stdin buffer?
Just because this is a common enough question, notwithstanding the links to other like questions and/or duplicates, I think this bears repeating:
If you are trying to fflush(stdin), you are almost certainly doing something wrong
An input buffer flush says, “I don’t care what you typed so far. I’m throwing it away and we’re starting over.”
Such behavior is exceedingly frustrating to users of a program. Users expect their inputs to be recognized and acted on — even if those inputs are wrong! By throwing it away, your program tells the user that their wishes are not respected, leaving them feeling like they have less control of their computer than they should.
It’s an XY problem
The usual suspect behind desires to clear the input is a scanf failure. The user typed something wrong, input is stuck with invalid characters, and the program needs to get past that.
But the program does not need to get any further than the next newline, which is to say, it only needs to discard stuff that was input before the user pressed Enter. This comes from one very simple principle:
The user will always press Enter after every prompted input
The solution comes in three forms:
Crash and burn. This is a strong signal that the user needs to fix his input to work, and actually provides him (or her) with a greater sense of control: the program does only what it is supposed to, accepting only input it should, and does nothing unless the input is correct!This also affords the user greater control in how he or she uses your program, because he can now supply prepared input to the program without having to babysit the keyboard.
Skip to the EOL and ask for the user to try again. This presupposes a human is frobbing the keyboard. But if you are going to go through this grief, then:
Read all input as a string, one line at a time, and endeavor to process that string (using sscanf, for example). This keeps your input and processing pointers all lined up and you will never have need to try to resynchronize with the input buffer.
Obviously, my opinion is that crash and burn is the most correct method for your usual console programs.
This is, in fact, part of the core design of Unix. This behavior enables individual utility programs to combine to perform useful scripted tasks.
The exception is a TUI program.
A text-based user interface is for a program that requires a human to operate it*, and takes over the terminal to behave in a different way, not requiring the user to press Enter to receive and act on input.
Games and text editors are examples of this kind of program.
* It is still possible to automate a TUI, of course, but the point is that the expected interaction with the program is no longer via newline-delimited streams, but rather via direct, individual key presses as input.
The C Standard says that fflush(stdin) has undefined behavior. You should not use it to flush pending input even on platforms where it seems to have the expected effect. There is no portable way to flush pending input, ie: all input that was typed before the prompt was issued.
If you just want to consume the characters typed by the user for a previous entry parsed with scanf(), up to and including the newline, you can use this simple function:
#include <stdio.h>
// Read and discard the rest of the current line of input.
// Returns EOF at end of file, otherwise returns a newline character
int flush_input(FILE *fp) {
int c;
while ((c = getc(fp)) != EOF && c != '\n')
continue;
return c;
}
If you are required to use scanf(), you can use these 2 calls:
scanf("%*[^\n]"); // read the remaining characters on the line if any.
scanf("%1[\n]"); // read the newline character if present.
Note however that you should not combine these 2 as a single call to scanf() because the first conversion will fail if the pending byte is a newline, and this newline will not be consumed by the second conversion.
Related
I learned C a few years ago using K&R 2nd edition ANSI C. I’ve been reviewing my notes, while I’m learning more modern C from 2 other books.
I notice that K&R never use scanf in the book, except on the one section where they introduce it. They mainly use a getline function which they write in the book, which they change later in the book, once they introduce pointers. There getline is different then gcc getline, which caused me some problems until i change the name of getline to ggetline.
Reviewing my notes i found this quote:
This simplification is convenient and superficially attractive, and it
works, as far as it goes. The problem is that scanf does not work well
in more complicated situations. In section 7.1, we said that calls to
putchar and printf could be interleaved. The same is not always true
of scanf: you can have baffling problems if you try to intermix calls
to scanf with calls to getchar or getline. Worse, it turns out that
scanf's error handling is inadequate for many purposes. It tells you
whether a conversion succeeded or not (more precisely, it tells you
how many conversions succeeded), but it doesn't tell you anything more
than that (unless you ask very carefully). Like atoi and atof, scanf
stops reading characters when it's processing a %d or %f input and it
finds a non-numeric character. Suppose you've prompted the user to
enter a number, and the user accidentally types the letter 'x'. scanf
might return 0, indicating that it couldn't convert a number, but the
unconvertable text (the 'x') remains on the input stream unless you
figure out some other way to remove it.
For these reasons (and several others, which I won't bother to
mention) it's generally recommended that scanf not be used for
unstructured input such as user prompts. It's much better to read
entire lines with something like getline (as we've been doing all
along) and then process the line somehow. If the line is supposed to
be a single number, you can use atoi or atof to convert it. If the
line has more complicated structure, you can use sscanf (which we'll
meet in a minute) to parse it. (It's better to use sscanf than scanf
because when sscanf fails, you have complete control over what you do
next. When scanf fails, on the other hand, you're at the mercy of
where in the input stream it has left you.)
At first i thought this quote was from K&R, but i cannot find it in the book. Then i realized thats it's from lecture notes i got online, for someone who taught a course years ago using K&R book.
lecture notes
I know that K&R book is 30 years old now, so it is dated is some ways.
This quote is very old, so i was wondering if scanf still has this behavior or has it changed?
Does scanf still leave stuff in the input stream when it fails? for example above:
Suppose you've prompted the user to enter a number, and the user
accidentally types the letter 'x'. scanf might return 0, indicating
that it couldn't convert a number, but the unconvertable text (the
'x') remains on the input stream.
Is the following still true?
putchar and printf could be interleaved. The same is not always true
of scanf: you can have baffling problems if you try to intermix calls
to scanf with calls to getchar or getline.
Has scanf changed much since the quotes above were written? Or are they still true today?
The reason i ask, in the newer books i am reading, no one mentions these issues.
scanf() is evil - use fgets() and then parse.
The detail is not that scanf() is completely bad.
1) The format specifiers are often used in a weak manner
char buf[100];
scanf("%s", buf); // bad - no width limit
2) The return value is errantly not checked
scanf("%99[\n]", buf); // what if use entered `"\n"`?
puts(buf);
3) When input is not as expected, it is not clear what remains in stdin.
if (scanf("%d %d %d", &i, &j, &k) != 3) {
// OK, not what is in `stdin`?
}
you can have baffling problems if you try to intermix calls to scanf with calls to getchar or getline.
Yes. Many scanf() calls leave a trailing '\n' in stdin that are then read as an empty line by getline(), fgets(). scanf() is not for reading lines. getline() and fgets() are much better suited to read a line.
Has scanf changed much since the quotes above were written?
Only so much change can happen without messing up the code base. #Jonathan Leffler
scanf() remains troublesome. scanf() is unable to accept an argument (after the format) to indicate how many characters to accept into a char * destination.
Some systems have added additional format options to help.
A fundamental issues is this:
User input is evil. It is more robust to get the text input as one step, qualify input, then parse and assess its success than trying to do all this in one function.
Security
The weakness of scanf() and coder tendency to code scanf() poorly has been a gold mine for hackers.
IMO, C lacks a robust user input function set.
int main() {
char one[50]; char two[50];
while (scanf("%10s %10s",one,two) == 2){
printf("%s %s\n",one,two);
}
}
The function won't let me leave the loop
Charanas-MacBook-Pro:Testing zone Charana$ ./a.out
first second
first second
one two
one two
one
two
one two
leave
Any idea how to fix this so that I can leave the scanf when I go to a new line, or only enter one word into terminal, not two?
The scanf() function skips white space before reading a string via %s. Newlines are white space. Therefore, it continues reading until it gets the second string. Similarly with numbers; if you ask for two numbers, scanf() won't stop reading until it gets two numbers, or it comes across a character that can't be a part of a number — it reads across newlines quite happily.
If you want line-based input, do not use scanf(); use the fgets() from standard C or
getline() from POSIX, coupled with
sscanf(). Then you can get sane behaviour.
As it stands, the scanf() function is working exactly as it is intended to work; it won't complete until it has a second string. You can interrupt the program, but that's usually not what you want. You can signal EOF (e.g. by typing Control-D on Unix-like platforms or Control-Z on Windows), but that can lead to further problems — you have to clear the EOF condition before you can get further input, for example.
Using fgets() and sscanf() has many advantages — go with it.
char keyin, buffer[1024];
do
{
keyin=gets(buffer);
}
while (keyin != "\n");
I have been trying to write a pause function in C where a user exits a paused state by pressing the Enter key. Where the user writes "pause" and this function gets executed. I have been working on this function for a while and it eludes me. I have implemented the code in several different ways and none of them work. I suspect it's because the comparison of "\n" with keyin. I think "\n" does not directly translate to the enter key.
You do not need a while loop to wait for a single Enter key press. It will wait (and you can press any key) until you press Enter: http://en.cppreference.com/w/c/io/gets
But you need to reserve a lot of space -- what if someone keeps pressing any other key "until something happens"? The buffer will overflow, and your program will (most likely) crash.
You probably want to use getchar -- this will send one keypress at a time back.
Note: the Enter key typically sends the ASCII code 13 (0x0D), not 10 (0x0A). You could use '\r' instead (minding others' notes on 'character' vs. "string"!), or prevent all confusion and use the hex or dec value.
This is different from the behavior of '\n' you are used to when outputting, because only certain functions will expand or convert the code '\n' in text to the required end-of-line sequence for your OS.
You cannot compare strings with == or != in C. You must compare the contents of the strings with a function like strcmp (or, preferably, one of the safer variants), e.g.:
while (strcmp(keyin, "\n") != 0);
As an aside, you should never ever actually use gets. It is impossible to use safely and securely.
I am beginning to design a shell application to run within a Linux terminal for a class I am taking.
This, of course, will involve reading variable-length input strings (commands) from the user. I know that I can simply read each command into a buffer of a size that I would consider appropriate, but this has the potential to either a) truncate the command or b) cause a buffer overflow.
If possible, how can way limit the length of user input to the console?
Say, if I set the command length to 3, 123 would be allowed, but if 123 were already present in the input string (before the user has pressed enter) and the user attempted to add 4, no character would print to the console, perhaps even with an 'error ping'.
I realize that I could design such functionality, but if that is needed, I am not sure where to start to do such a thing.
Either a pre-existing solution or advice on implementing my own solution would be greatly appreciated.
Edit:
I suppose a cheap and easy solution would be to read a command on character at a time until an enter signal is reached or the maximum length is reached. Would problems arise with a solution of this sort?
I have little experience with readline, but here's what you could try:
Write a function that checks rl_end (the number of characters in rl_line_buffer)
If you want to allow more, just return rl_getc
If not, you can use rl_ding
Set the rl_getc_function to call your function as described above
As a side note, if you do use readline, you don't need to limit the input at all (the library manages its memory as it goes). Another (simpler) function you might be interested in is getline.
That kind of low-level control of the console is not something that's included in C's rather basic built-in I/O model.
You need to look into something platform-specific, such as ncurses for Unix-like systems.
Without digging into platform-specific controls, you cannot limit how many characters a used may type in a console before hitting "Enter".
What you can do is check for the presence of a newline character in your input buffer; if it isn't there, then the user typed in more characters than you're prepared to deal with. You can reject that input, and then read stdin repeatedly until you see the newline.
Example:
#include <stdio.h>
#include <string.h>
...
char buf[SIZE];
...
printf("Gimme something: ");
fflush(stdout);
if (fgets(buf, sizeof buf, stdin))
{
char *newline = strchr(buf, '\n');
if (!newline)
{
printf("Input too long: \"%s\"\n", buf);
while (!newline && fgets(buf, sizeof buf, stdin))
newline = strchr(buf, '\n');
}
else
{
// do something with buf
}
}
In response to your edit, terminals are usually line-buffered, allowing users to enter as much as they want before hitting enter without you even knowing about it. You could set the terminal to raw or cbreak mode, but then you're entering platform-specific territory.
Instead, I would suggest that you avoid this problem, and accept that a terminal is a silly vestige from 2 million years ago. Most platforms define LINE_MAX to be the maximum line size any program needs to handle. Beyond that, you can simply assume your user is messing with you, and truncate.
I'm working on a final project for a C programming course. The project is to create a database to store drug information. I've completed all of the elements and now it's down to fine tuning everything.
There's a requirement in the project in the function where the user can change the drugs information. In the requirement, the user should be able to skip a field by hitting enter. For example, the user can change the producer of the drug and the quantity. If the user didn't want to change the producer, they'd hit enter and move onto the quantity.
I've looked around the internet and was able to let the user skip entering a string for the producer. However, I cannot get it work with an integer.
This is what I used so that the user can skip entering a string:
scanf("%30[^\n]", fentry[found].producer);
For clarity sake, fentry.producer is a string with 30 characters and found is an integer variable.
I've tried doing something similar with the integer input (EDIT: By integer input, I meant the one to enter the quantity, not the 'found' varible). It will let you skip entering something, but if you do enter something, it stores a random value.
Anyone know how to go about this?
Rather than using scanf(), a better way to get interactive input from a user is to use fgets(). This function gets a complete line of input up to where the user presses Enter, and returns it so you can parse it.
You can then use sscanf() to read the information out of the line, if you like. sscanf() works almost exactly like scanf() except it reads data from a string rather than from standard input.
Have you initialized the integer to 0 before the scanf?
What about this?
char s[50];
fgets(s, 50, stdin);
if(s[0] != '\n') {
fentry[found].producer = atoi(s);
}
You have to remove all spaces from s though.
scanf("%29s", fentry[found].producer); while('\n'!=getchar());
This works at all, is strictly ANSI C conform, max. portable.
Input buffer is cleared, chararray-length is supported, chararray contains ever a (terminating) '\0' (no initialization needed) and your array contains never the '\n' from user input.
I suspect the keyboard buffer is causing the problems in your code. The first time you call the code, it will read a maximum of 30 characters - so long as they are not the NEWLINE character. They will terminate when they read the NEWLINE. The NEWLINE will remain in the buffer.
However... if you call scanf in the same way again, the NEWLINE from the previous call will still be in the keyboard buffer. This will be read before requesting more input from the keyboard. The NEWLINE will immediately fail the scanf pattern, the char array will not be updated, and you will be left to a char pointer that points to an uninitialized area of memory.