scanf is not very forgiving in C - c

In the following code, I wish to give an option to the user to type 'e' to exit the code and see the results, instead of 100 as used in the code presently . But code is unable to run properly with 'e' from the terminal since scanf expects an integer, matching with the declared type. If I type in any character instead of an integer, scanf would store it in the variable in some manner. is that manner fixed? if it is fixed can I use that stored value to check if to exit program or not?
#include<stdio.h>
int main(void)
{
int rating[11], x;
for (int a = 0; a <= 11; a++)
rating[a] = 0;
while (1){
printf("key in 100 to see the results\n");
printf("Enter the rating on a scale from 1 to 10 ?");
scanf("%i", &x);
if ((x < 1 || x > 10) && x != 100)
printf("bad entry ! \n");
if (x == 100){
printf("\nRatings..............No of responses\n");
for (int y = 1; y <= 10; y++)
printf("%2i.......................%2i\n", y, rating[y]);
return 0;
}
rating[x]++;
}
}

scanf returns the number of items successfully read (or -1 on error). So, you could conceivably check to see if scanf returns 0, and bail in that case; it would exit the program if anything non-numeric was entered.
If you specifically want to check for 'e', the best bet is to fgets a whole line of input, check for your special cases using strcmp, then use atoi to get an integer.

Related

End while loop with ctrl+d, scanf?

I want the user to be asked "how many circles" they wanna write until the user decides to end it with (Ctrl+d) which is EOF?
extra question: if I write a letter for example "k" it will spam out circles. How do I change that?
#include <stdio.h>
int main ()
{
int i;
int x;
printf("\nHow many circles do you want to write?\n");
scanf("%d", &x);
while(x != EOF)
{
for (i = 1; i <= x; i = i + 1)
{
putchar('o');
}
printf("\nHow many circles do you want to write?"
"(to end program click ctrl+d at the same time!))\n");
scanf("%d", &x);
}
printf("\n\n Bye! \n\n");
return 0;
}
The biggest problem with your program is that scanf will not read an EOF into a variable. However, fixing just this problem is not going to make your program entirely correct, because there are other issues in your code:
Your code repeats itself - when possible, you should unify the code that deals with the first iteration vs. subsequent iterations.
Your code will not handle invalid input - when an end-user enters non-numeric data, your program goes into an infinite loop.
Your code follows the old style of C - declaring all variables at the top has not been required for more than fifteen years. You should declare your loop variable inside the loop.
Here is how you fix all these shortcomings:
int x;
for (;;) {
printf("\nHow many circles do you want to write? (to end the program click Ctrl+D at the same time!)\n");
int res = scanf("%d", &x);
if (res == EOF) break;
if (res == 1) {
... // Draw x circles here
} else {
printf("Invalid input is ignored.\n");
scanf("%*[^\n]");
}
}
printf("\n\n Bye! \n\n");
return 0;
As per the man page, scanf() will return EOF, not scan EOF to x as a value.
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs......
Also,
if I write a letter for example "k" it will spam out circles, how do I change that?
In case of input of one char value, it causes matching failure, in your case, scanf() returns 0, instead of 1.
So, altogether, you've to collect the return value of scanf() and check check that value for the required condition. You can change your code as
int retval = 0;
while ((retval = scanf("%d", &x))!= EOF && (retval == 1))
if you're allowed to #include , there are two convenient functions bool kbhit() and char getch().
So you can write
char c=0;
if(kbhit()) c = getch();
if(c== whatever code ctrl+d returns) x=EOF;
Hint: Take a look at what scanf(%d,&x) returns when you enter a letter instead of a number.
You can read char by char input :
#include <stdio.h>
int main ()
{
int i;
int x = 0;
int nb = 0;
while(x != EOF)
{
printf("\nHow many circles do you want to write?\n");
nb = 0;
for (x = getchar(); x != '\n'; x = getchar()) {
if (x == EOF)
goto end;
if (x >= '0' && x <= '9') {
nb = nb * 10 + x - '0';
}
}
for (i = 0; i < nb; i++)
{
putchar('o');
}
}
end:
printf("\n\n Bye! \n\n");
return 0;
}

Stop scanf loop if user enters a specific number (Not working) C

I've looked at multiple solutions but none of them worked for me.
I'm asking the user to enter numbers in a loop, but if the user enters a specific number the loop should break.
This is what I've got so far.
#include <stdio.h>
#include <stdlib.h>
#define MAXNUMBERS 5
int getNumbers(int array[])
{
int i;
int n = 0;
printf("Enter max. %d numbers, enter empty line to end:\n", MAXNUMBERS);
for (i = 0; i < MAXNUMBERS; i++)
{
scanf("%d", &array[i]);
fflush(stdin);
n++;
if (array[i] == '5')
{
break;
}
}
return n;
}
int main()
{
int array[MAXNUMBERS];
int amount_numbers;
amount_numbers = getNumbers(array);
printf("Numbers entered: %d\n", amount_numbers);
printf("First three: %d %d %d", array[0], array[1], array[2]);
return 0;
}
Input:
1
5
4
3
2
Output:
Numbers entered: 5
First three: 1 5 4
If the user enters 5 the loop should break.
I'm using 5 as an example, I later want it to do with an empty line. But it doesn't even work with 5.
It just keeps prompting the user to enter another number after he entered 5.
The actual problem is '5' != 5 the former is the character 5 which is in fact it's ascii value, and the latter is the number 5, since you are reading integers, i.e. using the "%d" specifier in scanf() you should use 5, but it would be better if it was just a int variable, and you could initialize it to any number you like before the loop starts.
Your loop is wrong anyway because if the user enters a non-numeric value then your program will invoke undefined behavior. Besides you already invoke undefined behavior with fflush(stdin), so
Remove fflush(stdin)1
7.21.5.2 The fflush function
If stream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is
undefined.
So the behavior is undefined for an input stream like stdin, or even if the most recent operation was input.
You must check that the value was read properly, and then check in the loop condition if it equals the value you want to stop the loop with, try this
int readNumber()
{
int value;
printf("input a number > ");
while (scanf("%d", &value) == 1)
{
int chr;
printf("\tinvalid input, try again...\n");
do { /* this, will do what you thought 'fflush' did */
chr = getchar();
} ((chr != EOF) && (chr != '\n'));
printf("input a number > ");
}
return value;
}
int getNumbers(int array[])
{
int i;
int stop = 5;
printf("Enter max. %d numbers, enter empty line to end:\n", MAXNUMBERS);
array[0] = 0;
for (i = 0 ; ((i < MAXNUMBERS) || (array[i] == stop)) ; i++)
array[i] = readNumber();
return i;
}
1This is a quote from the C11 draft 1570.
if (array[i] == '5')
You're checking whether array[i] is equal to the ASCII value of the character '5'.
Remove the '' to make it compare against the integer 5.
You are checking if an integer is equal to the character '5', which is then being cast to an ascii value of '5'.
Try using this:
if (array[i] == 5)
Disregard everything!
I should have written
if (array[i] == 5)
without the quotes!
I'm an idiot!
I sat 2 hours at this error...

How to allow user to exit while loop from terminal?

For example, if I want to write a code to average an unspecified number of numbers that the user enters, how can I make it so that the user can determine the number of numbers? ie. if the user wants to average just three numbers, he types them in one at a time, and then types in something to signal that this is it.
I wrote something like
while(i!=EOF){
printf("type in a number: \n");
scanf("%f",&i);
array[x]=i;
x++;
}
"and then some code to average the numbers in the array".
The idea was that if the user wants to signal that he finished entering numbers, he types in EOF and then the while loop will stop, however this isn't working. When I type in EOF at the terminal, it just writes "type in a number:" indefinitely.
scanf returns information in two different ways: in the variable i, and as its return value. The content of the variable i is the number that scanf reads, if it is able to return a number. The return value from scanf indicates whether it was able to read a number.
Your test i != EOF is fundamentally a type error: you're comparing the error indicator value EOF to a variable designed to hold a floating-point number. The compiler doesn't complain because that is accidentally valid C code: EOF is encoded as an integer value, and that value is converted to a floating-point value to perform the comparison. In fact, you'll notice that if you enter -1 at the prompt, the loop will terminate. -1 is the value of the EOF constant (on most implementations).
You should store the return value of scanf, and store it into a separate variable. If the return value is EOF, terminate the loop. If the return value is 1, you have successfully read a floating-point value.
If the return value is 0, the user typed something that couldn't be parsed. You need to handle this case appropriately: if you do nothing, the user's input is not discarded and your program will loop forever. Two choices that make sense are to discard one character, or the whole line (I'll do the latter).
double i;
double array[42];
int x = 0;
int r = 0;
while (r != EOF) {
printf("type in a number: \n");
r = scanf("%f", &i);
if (r == 1) {
/* Read a number successfully */
array[x] = i;
x++;
} else if (r == 0) {
printf("Invalid number, try again.\n");
scanf("%*[^\n]"); /* Discard all characters until the next newline */
}
}
You should also check that x doesn't overflow the bounds of the array. I am leaving this as an exercise.
I want to do it by typing in something that's not a number
Then get the input as a string, and exit if it cannot be converted to a number.
char buf[0x80];
do {
fgets(buf, sizeof(buf), stdin);
if (isdigit(buf[0])) {
array[x++] = strtod(buf);
}
} while(isdigit(buf[0]);
In case of no input scanf() does not set i to EOF but rather can return EOF. So you should analyze scanf() return code. By the way you can receive 0 as result which actually means there is no EOF but number cannot be read.
Here is example for you:
#include <stdio.h>
#define MAX_SIZE 5
int main()
{
int array[MAX_SIZE];
int x = 0;
int r = 0;
while (x < MAX_SIZE)
{
int i = 0;
printf("type in a number: \n");
r = scanf("%d",&i);
if (r == 0)
{
printf("ERROR!\n");
break;
}
if (r == EOF)
{
printf("EOF!\n");
break;
}
array[x]=i;
x++;
}
}
You cannot write 'EOF'.. since you are reading into a number...
EOF equals -1.. so if he enterd that, the loop would stop
You can test for the return value of the scanf function. It returns EOF on matching failure or encountering an EOF character.
printf("type in a number:" \n);
while(scanf("%f",&i)!=EOF){
array[x]=i;
x++;
printf("type in a number:" \n);
}

Scanf gets stuck

I'm having an issue with scanf() getting stuck, and the only way to continue the program is to type exit.
My input is something like this:
L 1 1 3 4 C 2 3 4
where the numbers are the parameters for L and C.
int main ()
{
// Get use input and put in array
int count, x;
int array[4];
char type;
scanf("%c", &type);
for (x = 0; x < 2; x++)
{
if (type == 'L')
{
for (count = 0; count < 4; count++)
{
scanf("%d", &array[count]);
}
} else if (type == 'C')
{
for (count = 0; count < 3; count++)
{
scanf("%d", &array[count]);
}
}
if (scanf("%*[ \n\t]%c", &type) == 0)
{
printf("Error");
break;
}
}
The scanf statement in question:
scanf("%*[ \n\t]%c", &type)
works fine if I'm not at the end of the line, but breaks otherwise. However, I won't know how many L and C objects I will have so can't rely on the value from the for loop.
I think your problem is here:
if (scanf("%*[ \n\t]%c", &type) == 0)
You're trying to check if you're out of characters to read, so you want to check if scanf returns EOF (which is -1, but just use the EOF constant). If you break when you see EOF returned you'll drop out of the loop as soon as you end input to the process (Ctrl+D on Linux, I believe Ctrl+Z Enter on Windows)
If your data is line-oriented, you would probably do better with reading lines using fgets() or possibly getline() from POSIX 2008, and then using sscanf() to parse the lines.

Dealing with unexpected input

Just a small query really, through the use of scanf, which in my case, will be scanning in X number of integers into variables, each integer separated by a space. Any hints/clues as to how to deal with input if when the integers are input, there are no spaces between them, for example, my input is such X X X X, but if XX X X was input, how could I deal with that within my scanf function?
Bearing in mind my scanf(%d %d %d %d"....)
Cheers.
I would read one value at a time with a counter and check whether a number is larger than 9, 99 or 999 to check for multiple digits. If you do, extract each digit with division and increase your counter for each digit.
You could check the return value of your scanf() to make sure it matches, and then validate that the values are between 0 and 9 after you receive them. Like so:
int vars[4];
if (scanf("%d %d %d %d", vars[0], vars[1], vars[2], vars[3]) != 4) {
// error
}
Then check each variable for being in range:
for (int i = 0; i < 4; i++) {
if (vars[i] < 0 || vars[i] > 9) {
// error
}
}
I'd just avoid scanf(). If each integer is just a single digit, something like the following would probably work:
int vars[4];
for (int i = 0; i < 4;) {
int c = getchar();
if (isdigit(c)) {
vars[i++] = c - '0';
} else if (!isspace(c)) {
// error
break;
}
}
The above does of course assume that the digits are '0' to '9' and have increasing, sequential values... and are each represented by a single char -- but those are probably safe assumptions.
While scanf reads after the enter button is pressed, it might be easier to read the line as a string and then try to analyze it. You can correct your input with backspace etc. on a fully featured terminal, so it's a bit more comfortable for user than getchar.
We look for single digits only, is that right?
Maybe something like:
char buffer[SOMECOUNT];
int digits[4];
int read, i;
scanf("%s", buffer);
for(int i = 0; i < strnlen(buffer, SOMECOUNT); ++i)
{
if( read >= 4 )
break;
if( isdigit(buffer[i]) )
{
digits[read] = buffer[i] - '0';
read++;
}
}
if ( read < 4 )
printf(error...);
Of course, this SOMECOUNT constant makes the solution a bit fragile for nasty input, so you may want to use the limit: scanf("%20s",buffer) or even construct the format string to include SOMECOUNT.

Resources