scanf for string and number in C - c

I've been trying to use scanf() for reading string and integer. It works fine, but I am unable to check if the input is given correctly.
char command[6];
int cmd_num;
scanf("%5s %d", command, &cmd_num);
It works fine for reading the right input, I am able to check if the string is right by strcmp. I tried to check the number by the function isdigit(), but it cannot check correctly, I guess because of whitespaces, but I am not sure exactly how it works.
I tried to google this, I played around with [^\n] but still it doesn't work. Could anyone enlighten me how scanf exactly works please?
I think solution would be if I exactly could tell scanf what to read ->string(space)integer. Is it possible to acquire this with regular expressions or any other way?
What I need basically is to scanf read the line recognize the string and then to check if there is a number after it too.
Another question is, I need to read the input for as long as the user is giving it, I tried to use getchar for while loop, to repeat as long as the char is not '\n' but this closes the loop right in the beginning, but if I give it EOF condition it never ends even with multiple newlines. I guess it could be limited by the scanf too, but I am not sure exactly how, threads on this forum couldn't give me the answer for it.
This will not procced:
while ( ( c = getchar() ) != '\n')
{
//code
}
This will proceed no matter of newlines:
while ( (c = getchar() ) != EOF )
{
//code
}

You don't check if a number is a number, and isdigit() is used to check if an ascii value is a number between 0 and 9.
To check that scanf() succeeded and the input is correct, thus the value of cmd_num is correct and is an integer read from the input, you have to check the return value of scanf().
It returns, the number of matched format specifiers. You have two of them so
if (scanf("%5s%d", command, &cmd_num) == 2) // this means it's correct
// ^ your code is missing the address of operator

Related

Scanf not working in while loop when nonmatching string is entered

I'm using a function called checkType to check whether the user has entered a valid input of integer type. For example, if the user enters 15 it will print valid and 15c will print not valid. However, if the user enters a string input only like ccccc, it results in an infinite loop and the program crashes. I have added some screenshots below to show the outputs.
int checkType(int input, char *c) {
if (input == 2 and *c == '\n') {
return 1;
} else {
return 0;
};
}
int main(void) {
int faces = 0;
char c;
int valid = 0;
int input;
while (valid == 0) {
printf("Enter number of faces: ");
input = scanf("%d%c", &faces, &c);
valid = checkType(input, &c);
printf(valid == 0 ? "not valid\n" : "valid\n");
}
}
Infinite Loop:
The scanf() family of functions is not really made for input of questionable syntax.
The usual approach to solve your problem is to read in all the input in a way which is sure to succeed, e.g. by reading a complete line (instead of a number, word-like string, or anything else with expected format) with fgets().
Then you can try to parse that line-representing string as by expected format. (You can use sscanf() for the parsing attempt.) If that fails you ignore it and read the next line, or try parsing according to an alternative allowed format for the same input.
The relevant difference is that reading a whole line will succeed and remove it from the input stream. In contrast to that, your code, in case of syntax failure, leaves in the input stream whatever did not match the expected syntax. As such it will, of course, fail the next iteration of the reading loop again. That is what causes your endless loop.
In detail:
read a whole line into a buffer,
using fgets() and the option to restrict the number of characters to the size of the buffer
at this point all of the line is removed from the input (aka stdin, aka input stream),
which means that the next read will get new input even if the read line does not match any allowed format,
this is the relevant difference to trying to read input directly with scanf()
from the line buffer, attempt to read separate values according to an allowed format,
using sscanf() and with a check of the return value
if successful great, you have your expected variables filled (e.g. an integer);
you could try scanning for additional, not format-covered trailing input (and continue like for incorrect input, even if the beginning of the line matched an allowed format)
if not successful try a different allowed format,
using sscanf() again,
from the same line buffer, which is not changed, not even if a format partially matched
if no allowed format matches the input it is time to consider it incorrect
incorrect input can be ignored or can cause a fatal parsing error in your program, your choice
However, if the user enters a string input only like ccccc,
it results in an infinite loop and the program crashes
Reason : scanf returns the number of valid read values from stdin. If we give cccc as input, scanf cannot read the value, because the input and variable faces are of different data type. This makes input = 0. So the function checkType returns 0, which in turn makes valid = 0. This makes while(valid == 0) always true and hence the endless loop.
Scanf will read form stdin, and not clean the stdin buffer if not match. So it will read wrong data again in your case, you can just add a clean function with stdin after scanf, like: __fpurge(stdin)
You can refer to the following two links:
https://man7.org/linux/man-pages/man3/scanf.3.html
http://c-faq.com/stdio/stdinflush2.html
With input like "ccccc" and scanf("%d%c" ..., there is no conversion to an int. scanf() returns 0 and stdin remains unchanged. Calling the function again has the same result.
Code neds to consume errant input - somehow.
Research fgets() to read a line of user input.

C code, scanf and getchar

I'm just asking what does the getchar do in this code and how does it work? I don't understand why the getchar affects the code, to me it seems as if its just getting the value but nothing is being done with the value.
int c=0;
while (c>=0)
{
scanf("%d", &c);
getchar();
}
Some possibilities of why getchar() might have been used there:
1) If it's done to ignore whitespaces (typically used when scanning chars with %c), it's not needed here because %d ignores whitespaces anyway.
2) Other possibility is that after this loop, some further scanning is done where the last \n left by the last call to scanf() might be a problem. So, getchar() might be used to ignore it.
3) In case you enter characters do not match %d, scanf() will fail. In that the characters you entered are left in the input stream and you'll never be able to read an int again (For example, if you input abcdddgdfg without that getchar() call). So, getchar() here will consume all those
chars (one per iteration) and eventually you'll be able to read int (using %d) again.
But this is all really not needed; it's just an attempt to fix flaws of scanf(). Reading inputs using scanf() and getting it correct is really difficult. That's why it's always recommended to use fgets() and parse using sscanf() or using strto*() functions if you are just scanning integers.
See: Why does everyone say not to use scanf? What should I use instead?
In this code, getchar is being called for its side effects: it reads a character from standard input and throws it away.
Probably this is reading input from the user. scanf will consume a number, but leave the newline character after the number untouched. The getchar consumes the newline and throws it away. This isn't strictly necessary in this loop, because the next scanf will skip over whitespace to find the next number, but it might be useful if the code after the loop isn't expecting to have a newline as the first thing on stdin.
This code is buggy, because it doesn't check for EOF, because it doesn't do anything sensible when the input is not a number or when there's more text on the line after the number, and because it uses scanf, which is broken-as-specified (for instance, it's allowed to crash the program if the input overflows the range of an int). Better code would be something like
char *linep = 0;
size_t asize = 0;
char *endp;
long c;
while (getline(&linep, &asize, stdin) > 0) {
errno = 0;
c = strtol(linep, &endp, 10);
if (linep == endp || *endp != '\0' || errno) {
puts("?Redo from start");
continue;
}
if (c == 0) break;
do_something_with(c);
}
free(linep);
Most likely the code is for reading in a list of integers, separated by a new line.
scanf will read in an integer, and put it into variable c.
The getchar is reading in the next character (assuming a new line)
Since it doesn't check, there is some potential that it wasn't a new line, or that scanf failed as the what it tried to read wasn't a number.
getchar(); is simply reading and consuming the character after the number, be it a space, comma, new line or the beginning of another integer or anything else.
IMO, this is not robust code. Good code would 1) at least test the result of scanf() and 2) test or limit the consumption of the following character to prevent "eating" a potential sign of the following number. Remember code cannot control what a user types, but has to cope with whatever is entered.
v space
"123 456"
v comma
"123,456"
v negative sign
"123-456"

Where to use this while loop?

I've just started to learn c programming. I don't know weather this question is silly or not.
Where do i use
while(getchar()! ='\n') ;
When using scanf function in some program the above mentioned while loop is used whereas some other program doesn't use the while loop after the scanf function.
So, where should i use this while loop and where not to?
Use the loop to clean up the rest of a line after an error
You use code similar to the loop in the question when you think there is debris left on a line of input after you (attempted to) read some data from standard input but the operation failed.
For example, if you have:
int n;
if (scanf("%d", &n) != 1)
…recover from error here?…
The error recovery should check for EOF (it is correct to use feof() here — but see while (!feof(file)) is always wrong for how not to use feof()), and if you didn't get EOF, then you probably got a letter or punctuation character instead of a digit, and the loop might be appropriate.
Note that you should always check that the scanf() operations — or any other input operation — succeeded, and the correct check for the scanf() family of functions is to ensure you got the expected number of successful conversions. It is not correct to check whether scanf() returned EOF; you can can get 0 returned when there's a letter in the input queue and you ask for a number.
Beware EOF
Incidentally, the loop in the question is not safe. It should be:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
If the loop in the question gets EOF, it continues to get EOF, and isn't going to do anything useful. Always remember to deal with it. Note the use of int c; too — getchar() returns an int and not a char, despite its name. It has to return an int since it returns every valid character code and also returns a distinct value EOF, and you can't fit 257 values into an 8-bit quantity.
While loop syntax :
while (condition test)
{
// C- statements, which requires repetition.
// Increment (++) or Decrement (--) Operation.
}
while(getchar()! ='\n') ;
Here your condition is getchar()!='\n'.
First getchar() will execute . It will ask input from user. This function returns the character read as an unsigned char cast to an int or EOF on end of file or error. While loop keep continue executing until return value will not match with '\n'
scanf() example : scanf("%s", &str1);
With scanf you can ask user for enter input one time only For more than one input you have to place scanf in loop.
Where as you ask to enter input to user in while loop condition So It will keep asking input from user until EOF signal occur.
When the scanf() format string does not cause it to read the newline from the input buffer.
Most simple format specifiers will not read the newline. However if you use %c, any character may be read, including newline, in which case the loop will need modification:
while( ch != '\n' && getchar() != '\n' ) ;
Where ch is the variable receiving the %c input.
Because console input is normally line-buffered, failing to consume the newline will cause subsequent stdin reading functions to return immediately without waiting for further input, which is seldom what is intended.
You would not use such a loop in cases where multiple fields are entered in a single line but processed in separate scanf() (or other stdin function) calls.

scanf() is getting skipped

Hi
Just started learning c for uni (usually use objective c) and have run into a strange issue with scanf, i have the following code
while(stringCheck == 0){
scanf("%c",&computerType);
computerType = toupper(computerType);
if ( computerType == 'L') {
/*set stringCheck to 1 so the scanf while loop breaks*/
stringCheck = 1;
counter = 0;
} else {
printf("ERROR\n");
}
}
This i printing out "ERROR" then asking for input (so it is skipping the scanf statment the first time). If i change the it to another variable that is a string it works fine, it stops on the first time.
The rest of the code works fine, its just the fact that it print an error as soon as it enters the loop that is annoying.
I have tried getChar() and it does the same thing.
Thank you for any help you have to offer.
If it's printing an error the first time you enter the loop, then there's already something in the input buffer. I'll guarantee (assuming your compiler is not brain-dead) that it is not skipping the scanf. You should check what it's actually receiving by changing:
printf("ERROR\n");
to:
printf("ERROR, code = %02x\n", computerType);
I would suggest that it's the newline from the last time your program ran that code (you say it's the first time but it's unclear as to whether you mean the first time into that loop ever (since program started) or first time entering that loop but you've been through it before on this program run.
When you enter LENTER the first time, your code will pull out the L but not the ENTER. If you then call that code again, it will get the ENTER key.
You should either understand and allow for what's actually entered or use a safe and sound input function like this one.
You should always check the return value from scanf(); it tells you how many of the conversions succeeded. In this context, if you don't get back 1, you have a problem. The first time around the loop, scanf() reads a character - but not an ell (l or L) because you say you get an error message. The next iteration attempts to read the newline or whatever else follows the previous erroneous character, and the newline is certainly not an ell, and the other characters quite likely aren't an ell either, thus producing at least one more error message. You would get an error printed for each non-ell character.
Generally, if you use scanf(), it is fairly hard to recover from errors. You're likely to be better off reading a line into a buffer (character array) and use sscanf() to parse it.
You should just need to add a space before %c. I am unsure why it works, but it does. This also happens with other data types.
Replace your scanf statement with scanf(" %c",&computerType);

Scanf and loops

here a a piece of code that is supposed to loop over and over until the user inputs a number between 0 and 15
int input;
do
{
printf("Enter a number => ");
scanf("%d",&input);
printf("\n %d \n",input);
}
while (!(input>0 && input <15));
However if the user puts in something like "gfggdf" it results in the loop repeating over and over and over and never prompting the user for input again... the console looks like this
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
(looping indefinitely)
After looking through this book, it seems I need to do something like this to prevent this from happening.
int input, error, status;
char skip_ch;
do
{
error = 0;
printf("Enter a number => ");
status = scanf("%d",&input);
printf("\n %d \n",input);
if(status!=1)
{
error=1;
}
else if(input < 0 || input >15){
error = 1;
}
do
{
scanf("%c",&skip_ch);
}while(skip_ch != '\n');
}
while (error);
I understand needing to check for the scanf status to make sure it was valid input, What I don't understand the the inner do-while loop. I feel like the book never really explained to me why scanf needs to be called several times like that, it's like somehow the scanf buffer got filled up with a bunch of garbage and it's somehow cleaning it out by just looping through it a million times for you.
Could anyone please explain this black magic to me?
That's why there are so many answers to similar questions that recommend not using scanf().
Also, you should check the return value from scanf() because it tells you when something has gone wrong - and no value could be converted.
The normal recommended solution is a combination of fgets() to read lines of input and sscanf() to parse the line once it is read.
The second loop in your example goes around reading the characters up to and including a newline, thus resynchronizing scanf() and skipping any bogus data. It is a long-winded way of doing it; it would be simpler to use getchar() instead:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
Note the use of 'int c;' - the type is crucially not 'char' or 'unsigned char' because it must store all possible char values plus EOF.
The original code loops indefinitely because the invalid data (the "gfggdf") is not removed from the input buffer when scanf fails to convert it to an integer -- it's left in the input buffer, so the next call to scanf looks at the same data, and (of course) still can't convert it to an integer, so the loop executes yet again, and the results still haven't changed.
The:
do {
scanf("%c",&skip_ch);
}while(skip_ch != '\n');
simply reads one character at a time (and ignores it) until it gets to the end of the line. If you prefer to get rid of the garbage in the input buffer without a loop, you can use something like: scanf("%*[^\n]");
Another possibility that's (probably more) widely used is to start by reading an entire line, then attempting to convert what's in the line. Whether it converts or not, you've already read all the data to the end of the line so the next attempt at reading/converting will start from the next line whether the conversion worked or not. This does have one potential weakness: if you got a really long line of input, you could end up reading and storing a lot of data for which you have no real use. It's rarely a big deal, but you should still be aware of it.
First of all, avoid using scanf(), use fgets() instead.
To evaluate your condition (first code). 0 > 0 is false. 0 < 15 is true. false && true is false. !false is true. That's why it's looping indefinitely.

Resources