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

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.

Related

Scanf: detect that the input was too long

We can easily limit the length of the input accepted by scanf:
char str[101];
scanf("%100s", str);
Is there any efficient way to find out that the string was trimmed? We could, for example, report an error in such case.
We could read "%101s" into char strx[102] and check with strlen() but this involves extra cost.
Use the %n conversion to write the scan position to an integer. If it was 100 past the beginning then the string was too big.
I find that %n is useful for all kinds of things.
I thought the above was plenty of information for anyone who had read the scanf docs / man page and had actually tried it.
The idea is that you make your buffer and your scan limit bigger than whatever size string you expect to find. Then if you find a scan result that is exactly as big as your scan limit you know it is an invalid string. Then you report an error or exit or whatever it is that you do.
Also, if you're about to say "But I want to report an error and continue on the next line but scanf left my file in an unknown position."
That is why you read a line at a time using fgets and then use sscanf instead of scanf. It removes the possibility of ending the scan in the middle of the line and makes it easy to count line numbers for error reporting.
So here is the code that I just wrote:
#include <stdio.h>
#include <stdlib.h>
int scan_input(const char *input) {
char buf[101];
int position = 0;
int matches = sscanf(input, "%100s%n", buf, &position);
printf("'%s' matches=%d position=%d\n", buf, matches, position);
if (matches < 1)
return 2;
if (position >= 100)
return 3;
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 2)
exit(1);
const char *input = argv[1];
return scan_input(input);
}
And here is what happens:
$ ./a.out 'This is a test string'
'This' matches=1 position=4
$ ./a.out 'This-is-a-test-string'
'This-is-a-test-string' matches=1 position=21
$ ./a.out '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' matches=1 position=100
You could use fgets() to read an entire line. Then you verify if the newline character is in the string. However, this has a few disadvantages:
It will consume the entire line, and maybe that's not what you want. Notice that fgets() is not equivalent to scanf("%100s") -- the latter only reads until the first blank character appears;
If the input stream is closed before a newline character is supplied, you will be undecided;
You have to go through the array to search for the newline character.
So the better option seems to be as such:
char str[101];
int c;
scanf("%100s", str);
c = getchar();
ungetc(c, stdin);
if (c == EOF || isspace(c)) {
/* successfuly read everything */
}
else {
/* input was too long */
}
This reads the string normally and checks for the next character. If it's a blank or if the stream has been closed, then everything was read.
The ungetc() is there in case you don't want your test to modify the input stream. But it's probably unnecessary.
fgets() is a better way to go, read the line of user input and then parse it.
But is OP still wants to use scanf()....
Since it is not possible to "detect that the input was too long" without attempting to read more than the n maximum characters, code needs to read beyond.
unsigned char sentinel;
char str[101];
str[0] = '\0';
if (scanf("%100s%c", str, &sentinel) == 2) {
ungetc(sentential, stdin); // put back for next input function
if (isspace(sentential) NoTrimOccurred();
else TrimOccurred();
else {
NoTrimOccurred();
}
A very rough but easy way of doing this would be, adding a getchar() call after the scanf().
scanf() leaves the newline into the input buffer after reading the actual input. In case, the supplied input is less than the maximum field width, getchar() would return the newline. Otherwise, the first unconsumed input will be returned.
That said, the ideal way of doing it is to actually read a bit more than the required value and see if anything appears in the buffer area. You can make use of fgets() and then, check for the 100th element value to be a newline or not but this also comes with additional cost.

How to stop enter character getting stuck in buffer? C

So in the following bit of code I'm reading an option from the user to then decide whether to perform a given action:
printf("Do you want to execute %s: ", exe);
char input = getchar();
if (input == 'y') {
return execute(exe);
} return 0;
The problem I'm having is that after the line char input = getchar() a character (I'm assuming an enter key or newline) gets stuck in stdin, and a future call to fgets() (reading from stdin) from my execute() function therefore eats up this stuck character and doesn't prompt the user for the required input.
Now I can define the following function to 'eat' any stray characters for me if called before calling execute()...
void iflush() {
int c;
while ((c = getchar()) != EOF && c != '\n') {
continue;
}
}
... and this solves the problem. But I'm left wondering why this is happening in the first place? Is there a more 'proper' way of getting a char from stdin without causing this stray character which then messes up my other methods? I'm very new to C and am keen to be sure I'm learning good habits.
It seems odd that I have to add a helper method, even if simple, to prevent errors in my program just from recording some user input. I've tried using fgetc() also but same problem.
I'm left wondering why this is happening in the first place?
It's because when you can characters with %c (or fgetc()), it doesn't ignore ay of the whitespaces in the input stream.
Some format specifiers, for example %d, ignore any whitespaces left in the input stream. So, it's not a problem. But %c doesn't ignore it.
Hence, you typically use those "eat" functions.
Think if you are scanning a whitespace character (such as space, newline, etc), how'd you able to scan it using scanf()?
Obviously, there are different and better ways such as using fgets().
I'm very new to C and am keen to be sure I'm learning good habits.
My suggestion is to avoid scanf() completely. Instead use fgets() to read a line and then you can parse that line using sscanf(). Please read: Why does everyone say not to use scanf? What should I use instead?
One thing to be aware about fgets() is if you input buffer has enough space, then it'll read the newline character as well,
which you would want to avoid in most user inputs. So, you need to check if the last character is a newline and then remove it.
You can achieve it using strcspn() function such as:
char line[256];
if (fgets(line, sizeof line, stdin) == NULL) {
/* handle failure */
}
line[strcspn(line, "\n")] = 0; /* remove the newline char if present */
If you are mainly interested in reading just one char, then an array 3 chars would be sufficient. Still using a larger buffer doesn't hurt:
char line[256];
char ch;
if (fgets(line, sizeof line, stdin) == NULL) {
/* handle failure */
}
if (sscanf(line, "%c", &ch) != 1) {
/* handle failure */
}
/* Now you can use 'ch' to check if it's 'y' */

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"

Read an indefinite amount of lines until user hits enter (twice) in C

I can't get around this problem. I need the user to type a string then hit enter, then another string. When he/she is done hit enter once more (this last string would only have \n character so I know when to stop).
char * buff = malloc (100);
printf("Type in strings, to finish hit enter\n");
do{
scanf (" %[^\n]",buff);
//do some other stuff with the string
} while(*buff);
printf("You have finished typing strings\n");
This approach I came up with is no use for me, since the [^\n] command is telling the function to read everything but the \n meaning that the \n is kept in the console buffer. If I simply do
while(*buff)
{
scanf ("%s",buff);
}
if I hit enter it does nothing.
Any other approach?
Yeah, scanf is actually looking for characters. What you want is gets (to get a line).
[edit] as pointed out by Daniel Fischer:
gets has (at last) been removed from the language. Even before, the
man page has for a long time said Never use gets()
Looks like my advice was not the best. I guess this means use fgets, as it protects against buffer overrun. Unlike gets, the newline character(s) will also be stored in the string and it is the programmer's responsibility to check for them.
const size_t bufsize = 100;
char buf[bufsize];
while( fgets(buf, bufsize, stdin) != NULL )
{
if( buf[0] == '\n' ) break;
/* Do something with your string... */
}
Besides what paddy pointed out (use gets to get a whole line), the condition
while(*buff);
won't ever be false, unless the user input would be a null character (as in ASCII value 0). The line break character (\n) has an ASCII value of 10, which evaluates to true in conditional statements.
Try this:
while(strlen(*buff) > 0);

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