C sscanf() force number input - c

I'm trying to use sscanf() to validate commandline input
I want only things like 1, 0.1, 0.111 to go through,
not a12 12a or 1.2a
I tried
if (sscanf(input, "%f", &val) != 1) { // to check for 1 successful conversion
printf("invalid input");
}
but it seems like I'm letting invalid inputs go through as well.
Any advice?

When sscanf(input, "%f", &var) encounters the input 12a or 1.2a, it stops at the invalid input part, leaving the remaining characters available for reading by a subsequent format code.
Consider that the following strings have invalid characters for floating point numbers following a valid number:
1.0 2.0 (a space)
1.0, 2.0 (a comma)
1.0a 2.0 (a letter)
Spaces and commas look OK to our human parsing engine; the machine floating point parsing simply stops when an invalid character is found, whether it is a human acceptable space or comma, or an ugly letter.
If you want to ensure that a floating point number is not immediately followed by garbage, you could use something like:
n = sscanf(input, "%f%c", &var, &ch);
if (n == 1 ||
(n == 2 && (ch == ' ' || ch == ',' || ...))) {
// ...
}

sscanf is not your friend, here. Even if you check the result to
ensure that you read one whole argument successfully, that doesn't
really guarantee that the input is valid in a meaningful sense.
Given input like:
1!!!1!oneone!
the %f parsing will stop (but succeed) after reading the first 1,
and leave the read pointer at the following !. This may not be what
you consider "valid input".
If anything on the same line is to be considered garbage, then it gets
difficult, because scanf doesn't distinguish between spaces and
newlines. Perhaps try something like this:
int main(int argc, char *argv[])
{
float val;
char c;
if (sscanf(argv[1],"%f%c",&val,&c)!=1)
{
printf("invalid input");
}
return 0;
}
now it will validate all commandline input if it's floating number input or not.

Related

goto statement in a switch statement causing infite loop [duplicate]

I came across this problem when I want to check what I input is number. The scanf function will return 1 if I successfully input a number. So here is what I wrote:
int argu;
while(scanf("%d",&argu)!=1){
printf("Please input a number!\n");
}
But when I input things like abcd to it, the loop would go forever and not stop for prompt.
I looked it up online and found that it had something to do with the cache and I need to clean it up so scanf can get new data. So I tried fflush but it didn't work.
Then I saw this:
int argu,j;
while(scanf("%d",&argu)!=1){
printf("Please input a number!\n");
while((j=getchar())!='\n' && j != '\n');
}
Then when I input things like 'abcd' it worked well and it prompted for my input. But when I input things like '12ab', it wouldn't work again.
So is there a way I can check the input for scanf("%d", &argu) is actually a number and prompt for another input if it isn't?
EDIT:
I saw the answers and solved my problem by using while(*eptr != '\n').
Notice that the fgets function actually reads '\n' into the array and gets doesn't. So be careful.
It's better to read a full line, using fgets(), and then inspecting it, rather than trying to parse "on the fly" from the input stream.
It's easier to ignore non-valid input, that way.
Use fgets() and then just strtol() to convert to a number, it will make it easy to see if there is trailing data after the number.
For instance:
char line[128];
while(fgets(line, sizeof line, stdin) != NULL)
{
char *eptr = NULL;
long v = strtol(line, &eptr, 10);
if(eptr == NULL || !isspace(*eptr))
{
printf("Invalid input: %s", line);
continue;
}
/* Put desired processing code here. */
}
But when I input things like abcd to it, the loop would go forever and not stop for prompt.
That's because if scanf encounters a character that does not match the conversion specifier, it leaves it in the input stream. Basically, what's happening is that scanf reads the character a from the input stream, determines that it's not a valid match for the %d conversion specifier, and then pushes it back onto the input stream. The next time through the loop it does the same thing. And again. And again. And again.
fflush is not a good solution, because it isn't defined to work on input streams.
For the input "12ab", scanf will read and convert "12", leaving "ab" in the input stream.
The best solution is to read all your input as text, then convert to numeric types using strtol (for integral values) and strtod (for real values). For example:
char input[SIZE]; // assume SIZE is big enough for whatever input we get
int value;
if (fgets(input, sizeof input, stdin) != NULL)
{
char *chk;
int tmp = (int) strtol(input, &chk, 10);
if (isspace(*chk) || *chk == 0)
value = tmp;
else
printf("%s is not a valid integer string\n", input);
}
chk points to the first character in the input stream that isn't a decimal digit. If this character is not whitespace or the 0 terminator, then the input string wasn't a valid integer. This will detect and reject inputs like "12ab" as well as "abcd".
scanf is a good solution if you know your input is always going to be properly formed and well-behaved. If there's a chance that your input isn't well-behaved, use fgets and convert as needed.
I will suggest to get input as a string and check for non-numeric characters in it. If input is valid convert string to int by sscanf(str,"%d",&i); or else diplay error.
Just call scanf("%*[^\n]\n") inside the loop, and it will discard the "cache".
Call scanf("%*[^\n]\n") inside the loop. This should be enough to discard anything associated with the cache.

Line wise input in c

how can I enforce following input restriction in c?
First line contains float ,
Second line contains float ,
Third line int,
after pressing enter three times in console, program should be able to read each line and put the contents in respective int,int,float variables.
After three enter key press program should not wait for user input and start validation.
some test cases
line1: 34
line2:4
line3:12
result: ok
line1:
line2:4
line3:12
result: not ok
line1: Hi
line2:4
line3:12
result: not ok
so far I used the basics
scanf("%f",&p);
scanf("%f",&r);
scanf("%d",&t);
it works fine for test case 1 and 3, but fails when I leave an empty line.
You should always check the return value of scanf.
The reason is that the return value is what scanf uses to communicate conversion errors, among other errors. For example, if your program tells scanf to expect a sequence of decimal digits, and scanf encounters something that doesn't match that pattern, the return value will indicate this failure.
The value returned will be the number of items that are successfully assigned to. For example,
char str[128];
int x, y = scanf("%d %127s", &x, str);
If y is 1, then it should be assumed that x is safe to use. If y is 2, then it should be assumed that both x and str are safe to use.
This answers part of your question. The next part is how you can go about ensuring that the input is in the form of lines. scanf doesn't strictly deal with lines; it deals with other units, such as %d being an int encoded as a sequence of decimal digits (and a sign); it'll return once the decimal digit sequence ends... There's no guarantee that the decimal digits will occupy the entirety of the line.
There are actually two problems here: leading and trailing whitespace. All format specifiers, with the exception of [, c, C, and n, will cause leading whitespace to be discarded. If you want to handle leading whitespace differently, you'll need to codify how you expect leading whitespace to be handled.
Consider that discarding user input is almost always (if not always) a bad idea. If you don't care what the remainder of the line contains, you could use something like scanf("%*[^\n]"); getchar(); to discard everything trailing up to and including the '\n' newline character... The first statement would attempt to read as many non-newline characters as possible, and the second would discard the terminating newline character. However, if you want to ensure that the input occupies the entirety of the line, then you need to test the value returned by getchar.
An example using all of these considerations:
/* Test for leading whitespace (including newlines) */
int c = getchar();
if (c != '-' && !isdigit(c)) {
/* Leading whitespace found */
}
ungetc(c);
/* Test for correct data conversion */
int x, y = scanf("%d", &x);
if (y != 1) {
/* Something non-numeric was entered */
}
/* Test for trailing newline */
c = getchar();
if (c != '\n') {
/* Trailing newline found */
}
Armed with this information, perhaps you can come up with an attempt and update your question with some code if you have any problems...
P.S. I noticed in the code you wrote, you seem to have %f and %d confused; %f is for reading into floats, and %d is for reading into ints, not the other way around...
As soon as I read line wise input, I know that fgets + sscanf must be used instead of direct scanf. Of course you can use getc/getchar as a workaround, but you can get corner cases, where I findfgets + sscanf cleaner. Example to get a float alone on a line:
char line[80], dummy[2];
float val;
if (fgets(line, sizeof(line), stdin) == NULL)...
if (sscanf(line, "%f%1s", &val, dummy) != 1)...
// Ok val was alone on the line with optional ignored blanks before and/or after
You could also add a test for loooong lines :
if ((line[0] != 0) && (line[strlen(line)-1 != '\n'))...

Why does this if condition not work even if I enter the correct data?

#include < stdio.h >
#include < process.h >
rec();
main() {
int a, fact;
char question, n, y;
do {
printf("\nEnter any number ");
scanf("%d", & a);
fact = rec(a);
printf("Factorial value = %d\n", fact);
printf("do you want to exit.....(y/n):");
scanf("%s", & question);
}
while (question == n);
exit(0);
}
rec(int x) {
int f;
if (x == 1) return 1;
else f = x * rec(x - 1);
return f;
}
In this program I want to get factorial of the entered number, which I get. But I also want the user to say whether to exit or get the factorial of another number, which I can't do. It asks user but when I enter "n" it exits.
Where is the error?
You want
while (question == 'n');
Or
char question, n = 'n', y = 'y';
Though I find the 2nd version a little redundant.
Either way you need to change
scanf("%s"
to
scanf("%c"
To correctly read in a single char and not a string. Thanks RageD
One problem is the combination of:
char question, n, y;
scanf("%s", &question);
You are using %s to read a null-terminated string into a single character. Even if you hit 'y' and return, you'll be overwriting beyond the end of the variable. This is not good. (The good news is that "%s" skips over white space, including the newline after the number).
You either need to use "%c" in the format:
char question;
scanf(" %c", &question); // NB: The leading space is important!
or you need to use a string format and a string variable (and no &):
char question[10];
scanf("%9s", question);
If you use an array, you need to consider whether to use strcmp(), or whether to compare the first character from the input:
while (strcmp(question, "n") == 0);
while (question[0] == 'n');
You probably got told by the compiler that you'd not declared variable n so you added it. You probably need the loop to end with while (question == 'n');and then get rid of the (now) unused variablen(and the currently unused variabley`).
Note that if you use omit the space in the " %c" format string:
scanf("%c", &question);
then it will normally get the newline after the number, which won't be 'n', so your loop will exit every time, apparently without waiting for you to enter anything. You can finesse that with scanf(" %c", &question); which skips white space before reading a character.
You should test that scanf() received the input you expected each time you use it. The correct test for single item inputs is:
if (scanf(" %c", &question) != 1)
...input failed...
If you need to distinguish between EOF and conversion failure, you can capture the return from scanf():
int rc;
if ((rc = scanf(" %c", &question)) != 1)
...rc == EOF on EOF; rc == 0 on 'conversion failure'...
...a single character input can't easily fail...
...but if someone types 'a' instead of '9' when you're looking for a number...
Getting I/O right using scanf() is distressingly hard. Many experienced programmers simply don't use it; it is too hard to get right. Instead, we use fgets() or POSIX getline() to read a line of data, and then use sscanf() to parse it. There are many advantages to this, but a primary one is that the newline has been eaten so you don't run into problems with the variable question not containing the answer you expect.

How do I validate that the input is numerical?

I have an assignment where the user must enter four inputs, one after another. They are:
character, float, float, int.
The main issue is how to check for errors and make sure the used entered valid input?
I have finished the character section but for the floats and ints, how can I check that only numbers are entered and print an error message if letters or symbols are entered?
Thought maybe isdigit() or isaplha() but unsure how to implement their use.
NOTE I have already used scanf() for the input but not sure how to check if input is valid?
If the user is required to enter a string, two floating point numbers and an integer, use
char s[1024];
float f1, f2;
int i;
if (sscanf (buff, "%s %f %f %d", s, &f1, &f2, &i) == 4) {
/* Could scan values as expected. */
} else {
/* Input not as expected. */
}
since sscanf returns the number of successfully scanned values. For the details, see the sscanf manual page. Note that scanning an unbounded string with %s has its problems with large inputs. This may not be an issue for homework assignments, but is definitely something to be aware of in production software.
With sscanf(), you can try to parse the content of a string as some data type, like an integer (with the %d format specifier) or floating point number (with %g).
The return value of sscanf() tells you if it was successful in interpreting the text as the desired data.
You can also use %n to learn how many characters sscanf() looked at, which is handy when you want to analyze in multiple steps.
I don't know how you're getting your values right now other than you're using scanf() as you mentioned in your post. So lets say you're doing something like this:
char buf[100];
scanf("%s", buf);
to get the float/int values. If you want to use isdigit() to verify they are all digit values you can loop as such:
int i = 0;
//need to check for a . for floats
//need to check for a - for negative numbers
while(isdigit(buf[i]) || buf[i] == '.' || buf[i] == '-')
i++;
if(i == strlen(buf)) // if we made it to the end of the string
//we have all digits, do all digit code
else
//there are numbers or symbols, ask for the number again, or terminate, or whatever

what happens when you input things like 12ab to scanf("%d",&argu)?

I came across this problem when I want to check what I input is number. The scanf function will return 1 if I successfully input a number. So here is what I wrote:
int argu;
while(scanf("%d",&argu)!=1){
printf("Please input a number!\n");
}
But when I input things like abcd to it, the loop would go forever and not stop for prompt.
I looked it up online and found that it had something to do with the cache and I need to clean it up so scanf can get new data. So I tried fflush but it didn't work.
Then I saw this:
int argu,j;
while(scanf("%d",&argu)!=1){
printf("Please input a number!\n");
while((j=getchar())!='\n' && j != '\n');
}
Then when I input things like 'abcd' it worked well and it prompted for my input. But when I input things like '12ab', it wouldn't work again.
So is there a way I can check the input for scanf("%d", &argu) is actually a number and prompt for another input if it isn't?
EDIT:
I saw the answers and solved my problem by using while(*eptr != '\n').
Notice that the fgets function actually reads '\n' into the array and gets doesn't. So be careful.
It's better to read a full line, using fgets(), and then inspecting it, rather than trying to parse "on the fly" from the input stream.
It's easier to ignore non-valid input, that way.
Use fgets() and then just strtol() to convert to a number, it will make it easy to see if there is trailing data after the number.
For instance:
char line[128];
while(fgets(line, sizeof line, stdin) != NULL)
{
char *eptr = NULL;
long v = strtol(line, &eptr, 10);
if(eptr == NULL || !isspace(*eptr))
{
printf("Invalid input: %s", line);
continue;
}
/* Put desired processing code here. */
}
But when I input things like abcd to it, the loop would go forever and not stop for prompt.
That's because if scanf encounters a character that does not match the conversion specifier, it leaves it in the input stream. Basically, what's happening is that scanf reads the character a from the input stream, determines that it's not a valid match for the %d conversion specifier, and then pushes it back onto the input stream. The next time through the loop it does the same thing. And again. And again. And again.
fflush is not a good solution, because it isn't defined to work on input streams.
For the input "12ab", scanf will read and convert "12", leaving "ab" in the input stream.
The best solution is to read all your input as text, then convert to numeric types using strtol (for integral values) and strtod (for real values). For example:
char input[SIZE]; // assume SIZE is big enough for whatever input we get
int value;
if (fgets(input, sizeof input, stdin) != NULL)
{
char *chk;
int tmp = (int) strtol(input, &chk, 10);
if (isspace(*chk) || *chk == 0)
value = tmp;
else
printf("%s is not a valid integer string\n", input);
}
chk points to the first character in the input stream that isn't a decimal digit. If this character is not whitespace or the 0 terminator, then the input string wasn't a valid integer. This will detect and reject inputs like "12ab" as well as "abcd".
scanf is a good solution if you know your input is always going to be properly formed and well-behaved. If there's a chance that your input isn't well-behaved, use fgets and convert as needed.
I will suggest to get input as a string and check for non-numeric characters in it. If input is valid convert string to int by sscanf(str,"%d",&i); or else diplay error.
Just call scanf("%*[^\n]\n") inside the loop, and it will discard the "cache".
Call scanf("%*[^\n]\n") inside the loop. This should be enough to discard anything associated with the cache.

Resources