I should mention that this does work in a different environment: in our programming class we normally use the IDE codeblocks (which is find awful), so I just use the gcc compiler and vim in my terminal (I'm on arch). I didn't encounter problems until recently, when I had to read in a string which contained spaces. For that I thought using the fgets() function would be a good idea, but it created some problems. This is what the code looks like:
void addStudent() {
struct Student student;
printf("Name of student: ");
fgets(student.name, 25, stdin);
}
This however does not prompt me for input in my shell, it simply continues and reads in a newline character \n immediately. Do you guys have any idea how to fix this?
As pointed out by the comments, it's not good to combine a function like scanf() with fgets(). When scanf() is called it leaves a newline character in the input buffer which is then immediately read by fgets(), causing it to fail prompting the user.
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.
If the code is
scanf("%s\n",message)
vs
gets(message)
what's the difference?It seems that both of them get input to message.
The basic difference [in reference to your particular scenario],
scanf() ends taking input upon encountering a whitespace, newline or EOF
gets() considers a whitespace as a part of the input string and ends the input upon encountering newline or EOF.
However, to avoid buffer overflow errors and to avoid security risks, its safer to use fgets().
Disambiguation: In the following context I'd consider "safe" if not leading to trouble when correctly used. And "unsafe" if the "unsafetyness" cannot be maneuvered around.
scanf("%s\n",message)
vs
gets(message)
What's the difference?
In terms of safety there is no difference, both read in from Standard Input and might very well overflow message, if the user enters more data then messageprovides memory for.
Whereas scanf() allows you to be used safely by specifying the maximum amount of data to be scanned in:
char message[42];
...
scanf("%41s", message); /* Only read in one few then the buffer (messega here)
provides as one byte is necessary to store the
C-"string"'s 0-terminator. */
With gets() it is not possible to specify the maximum number of characters be read in, that's why the latter shall not be used!
The main difference is that gets reads until EOF or \n, while scanf("%s") reads until any whitespace has been encountered. scanf also provides more formatting options, but at the same time it has worse type safety than gets.
Another big difference is that scanf is a standard C function, while gets has been removed from the language, since it was both superfluous and dangerous: there was no protection against buffer overruns. The very same security flaw exists with scanf however, so neither of those two functions should be used in production code.
You should always use fgets, the C standard itself even recommends this, see C11 K.3.5.4.1
Recommended practice
6 The fgets function allows properly-written
programs to safely process input lines too long to store in the result
array. In general this requires that callers of fgets pay attention to
the presence or absence of a new-line character in the result array.
Consider using fgets (along with any needed processing based on
new-line characters) instead of gets_s.
(emphasis mine)
There are several. One is that gets() will only get character string data. Another is that gets() will get only one variable at a time. scanf() on the other hand is a much, much more flexible tool. It can read multiple items of different data types.
In the particular example you have picked, there is not much of a difference.
gets - Reads characters from stdin and stores them as a string.
scanf - Reads data from stdin and stores them according to the format specified int the scanf statement like %d, %f, %s, etc.
gets:->
gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with a null byte ('\0').
BUGS:->
Never use gets(). Because it is impossible to tell without knowing the data in advance how many characters gets() will read, and because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use. It has been used to break computer security. Use fgets() instead.
scanf:->
The scanf() function reads input from the standard input stream stdin;
BUG:->
Some times scanf makes boundary problems when deals with array and string concepts.
In case of scanf you need that format mentioned, unlike in gets. So in gets you enter charecters, strings, numbers and spaces.
In case of scanf , you input ends as soon as a white-space is encountered.
But then in your example you are using '%s' so, neither gets() nor scanf() that the strings are valid pointers to arrays of sufficient length to hold the characters you are sending to them. Hence can easily cause an buffer overflow.
Tip: use fgets() , but that all depends on the use case
The concept that scanf does not take white space is completely wrong. If you use this part of code it will take white white space also :
#include<stdio.h>
int main()
{
char name[25];
printf("Enter your name :\n");
scanf("%[^\n]s",name);
printf("%s",name);
return 0;
}
Where the use of new line will only stop taking input. That means if you press enter only then it will stop taking inputs.
So, there is basically no difference between scanf and gets functions. It is just a tricky way of implementation.
scanf() is much more flexible tool while gets() only gets one variable at a time.
gets() is unsafe, for example: char str[1]; gets(str)
if you input more then the length, it will end with SIGSEGV.
if only can use gets, use malloc as the base variable.
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.
I'm it really sure how to explain this question but i'll give it a try.
I have this line of code in my program:
scanf (" %c", &character);
Notice the space before %c. This is supposed to keep the scanf from interpreting the last ENTER key as a character. This works, however after this line and its resulting printf output the program will not continue to execute until I provide another character and then press the ENTER key. After that it ignore all scanf and prints all printf output with the absence of any user input. Does that make sense. Im new to C so im sure im missing something simple but I couldn't find any solutions.
Thanks!
The standard input is buffered.
Your code will not see the buffer until it (the standard input) is flushed.
This happens when the buffer becomes full or the '\n' (Enter Key) is inserted.
Because scanf() is a blocking I/O call. The application will wait for input (indefinitely). Thus making the code look like it has stalled.
IMHO
You should not try and interpret what has happend behind you in previous parts of the code. This will couple your code to other input that you may have no control over. Your input should read everything it need to and no more.
For example: If you ask the user to enter a character then hit enter. You should remove the character then enter from the input stream. You should not expect the next user of the stream to tidy up for you (or compensate for your code).
printf("Enter (Y/N)\n");
scanf("%c", &character);
scanf("%*[^\n]"); // Ignore everything to the end of line.
// Assuming you have told the user you will ignore
// extraneous characters. The next input should now
// expect only what they need.
Try this:
scanf ("\n%c", &character);
It should "consume" the \n character in buffer and then wait for a new char.
In your previous scanf call, use something like scanf("%c\n", &character); to eat the newline as it is entered. This is typically less error-prone than waiting to handle it the next time you need input.