End while loop with ctrl+d, scanf? - c

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;
}

Related

Why does my program print something before it ends when I press ctrl + D?

So I wrote a simple program that converts a decimal to binary, that only accepts positive whole numbers. So numbers like -2 and 1.1 would output "Sorry, that's not a positive whole number." It infinitely asks the user to input a number until the user presses ctrl + D. However when I tested it it prints out the "Sorry..." statement before it ends the program.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
void DecToBin(int userInput){
int binary[32];
int i = 0;
while (userInput > 0) {
binary[i] = userInput % 2;
userInput /= 2;
i++;
}
for (int j = i - 1; j >= 0; --j) {
printf("%d", binary[j]);
}
}
int main(void) {
double userDec;
int temp;
printf("Starting the Decimal to Binary Converter!\n\n");
while(!feof(stdin)) {
printf("Please enter a positive whole number (or EOF to quit): ");
scanf("%lf", &userDec);
temp = (int)(userDec);
if ((userDec > 0) && (temp / userDec == 1)) {
printf("\n\t%.0lf (base-10) is equivalent to ", userDec);
DecToBin(userDec);
printf(" (base-2)!\n\n");
}
else {
printf("\tSorry, that was not a positive whole number.\n");
}
}
printf("\n\tThank you for using the Decimal to Binary Generator.\n");
printf("Goodbye!\n\n");
return 0;
}
(All the tab and newlines are just how it's supposed to be formatted so don't pay attention to that)
So from what I'm understanding, my program reads ctrl + D as the else in my while loops. So, any idea why that is?
It seems like you think C-d would trigger some kind of break in the code. Like the keyword break. This is not true.
Read this post to see what's happening when you press C-d: https://stackoverflow.com/a/21365313/6699433
That does not cause anything special to happen in the C code. scanf will simply not read anything. After the scanf statement, the code will continue as usual, so the code WILL unconditionally enter the if statement.
This is also a pretty severe thing, because you'll be using userDec uninitialized. scanf returns the number of successful assignments, and you should always check the return value. So in your case you want this:
if(scanf("%lf", &userDec) != 1) { /* Handle error */ }
Because if scanf does not return 1, userDec is unassigned.
To achieve what you want, simply do this:
if(scanf("%lf", &userDec) != 1)
break;

C - prompt user for valid int causes endless loop when input is not an int

int main(void) {
int x;
do {
printf("Height: ");
} while(!((scanf("%d", &x)) == 0) && !(x > 0 && x < 23));
}
I want to simply prompt a user to enter a number between 0 and 23, and if the input is invalid (not an int or out of range), I want to prompt again until they do it right. However, the first time I enter an invalid integer, I get "Height" printed over and over again in an endless loop. How can I prompt them until it the input is valid?
It works when I enter a number out of range, but not when I enter a string like "abc"
scanf will return 1 if, and only if, the user has inputted something that can be converted into an int.
If scanf does not return 1, then it's your job to clear the stream so another value can be read in. You do that with the idiomatic
{
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
It's this second thing you are not doing that appears to be causing your code to print endlessly.
There are more succinct ways but this is a starting point:
int main(void) {
int x;
do {
printf("Height: ");
if (scanf("%d", &x) != 1){
// invalid input, clear the stream and go round again.
int c;
while ((c = getchar()) != '\n' && c != EOF);
continue;
}
} while (x < 0 || x > 23);
return x;
}
(Some folk don't like an empty while loop written in the way I have. Adjust accordingly if it's not to your taste: some firms ban it.)
Your error is in your truth statement. It should probably be something like while (scanf("%d", &x) == 1 && (x < 0 && x > 23)). scanf returns the number of variables it successfully assigned.#
But I recommend you read the value of inside the loop statement itself and not the truth statement. It's just hard to reason about when it it's in the truth statement and you've fallen into a pitfall doing that.

C Program - Do - while loops not working

My target output is after users entering a number >2 & <20 (result show) then program continue asking users enter another number. Or if users enters number <=2 or >=20, it will not show result but just re-asking users to enter number.
My Current Output: If I input number <=2 || >=20, it will re-ask. but if I enter number between 2 and 20. It will just stops which suppose to be keep asking for entering new numbers.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 20
int main(void)
{
unsigned int random_array[MAX][MAX];
unsigned int r, c, x, y;
do {
printf("Number Matrix in array ? ");
scanf("%d", &c);
system("cls");
r = c;
if (c>2 && c<20) {
r = c;
for (x = 0; x <= r - 1; ++x)
{
for (y = 0; y <= c - 1; ++y)
{
random_array[x][y] = -1;
}
}
for (x = 0; x <= r - 1; ++x)
{
for (y = 0; y <= c - 1; ++y)
{
if (x == y)
random_array[x][y] = 0;
else
if (x<y)
random_array[x][y] = 1;
printf("%4d", random_array[x][y]);
}
puts("");
}
system("pause");
}
} while (c<=2 || c >=20);
return 0;
}
Since you want to prompt the user for input regardless of what they last input, you probably need an infinite loop. For this, replace your line with the while condition to this:
} while (1);
This basically tells your program to loop infinitely.
Your codes
do
{
// show something
} while (c<=2 || c >=20);
means that it will stop after showing something if (c > 2 && c < 20), and that is exactly why your program quits after the condition is met.
To achieve your goal, consider using an infinite loop, and do different things using if-else inside the loop.
printf("Number Matrix in array ? ");
scanf("%d", &c);
system("cls");
if (c<=2 || c >=20)
{
continue;
}
else
{
// show something
}
The while condition should be while(c>2&c<20). But if you enter a number <=2 or >=20 the program will end and will not ask you for an input anymore. So the solution would be to use an infinite while loop and use break to end the loop when you want by using a condition.
A do-while loop will run the block of code once, then will repeat until the while conditional evaluates to false.
do{
//Stuff
}while (c > 20 || c < 2);
That would do the //Stuff part once, then it would do it again until c is either greater than 20 or less than 2.
What you want to do is surround the entire thing in an infinite loop, (either for(;;) or while(1)) so that it continues regardless.
But you also want to validate the input, so that's when you could use a do-while loop. When you're getting the scanf, you could do something like:
do{
printf("Enter c: ");
scanf("%d", &c);
}while (c > 20 or whatever);
Then you could make him keep putting in c until it's the desired input!
Hope this helps.
EDIT: Here's an example of putting the do-while inside the while:
while(1){
do{
printf("Enter a positive number: ");
scanf("%d", &aNum);
}while(aNum < 0);
printf("Your positive number is %d.\n", aNum);
}
That would ask a user for input, and if he puts in a negative number it would ask him again. If it's a positive number it would print, then go back to the start and ask him for an input again.

Print output just beside the user input

According to the question, The user needs to enter the no of hours the vehicle is parked and the total charge for the hours should get printed beside it.
for example:
I created this simple program
#include<stdio.h>>
#include<math.h>
float calculateCharges(float hurs);
int main()
{
float hours;//total no of hours vehicle is parked
int i;
printf("%s%10s%10s", "Car", "Hours", "Charges");
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
scanf("%f", &hours);
printf("\t%f\n", calculateCharges(hours));
}
getch();
return 0;
}
float calculateCharges(float hurs)
{
float charges;
hurs = ceil(hurs);
if (hurs >= 24) charges = 10;
else
{
if (hurs <= 3) charges = 2;
else
{
hurs = hurs - 3;
charges = 2 + 0.5*hurs;
}
}
return charges;
}
But now every time I enter hours the charges are getting printed below it instead of beside it. As shown in the image:
Is there is a way to consume the newline after scanf? So that charges can be printed beside the scanf?
I have modified my code this way too, but it didn't make any difference.
printf("%s%10s%10s", "Car", "Hours", "Charges");
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
printf("\t%f\n",(scanf("%f", &hours),calculateCharges(hours)));
}
Let me know if the original question is required. I'm using Visual studio 2017 RC.
You can use something like this:
#include <iostream>
#include <windows.h>
//This will set the position of the cursor
void gotoXY(int x, int y) {
//Initialize the coordinates
COORD coord = {x, y};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
return;
}
void getCursorXY(int &x, int&y) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
x = csbi.dwCursorPosition.X;
y = csbi.dwCursorPosition.Y;
}
}
I found it here.
As already written in one of the answers this solution is not platform independent.
But i guess there are similar solutions on other platforms and you can easy set the cursor on the position you want.
Example usage in your main:
for (i = 1; i <= 3; i++)
{
printf("\n%d\t", i);
scanf("%f", &hours);
gotoXY( 20, i + 1);
printf("\t%f\n", calculateCharges(hours));
}
Workarounds for scanf can be found here.
scanf_s always generates a new line upon enter and unfortunately other user input capturing platform independent functions I know of (getc & getchar) do so too. Anyway on Windows it could be done using _getch() from conio header.
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
int getIntFromUser()
{
char readCharacters[10];
int index = 0;
for (int currentChar = _getch(); currentChar != '\r'; currentChar = _getch())
{
if (currentChar == EOF)
{
// Some error that shouldn't occour in your simple homework program
}
if (index > 9)
{
// Another possible error case where you would start to write beyond 'readCharacters' array
}
// We might as well disallow anything but digits, enter & backspace (You don't need anything else, do you?)
if ((currentChar < '0' || currentChar > '9') && currentChar != '\b')
{
continue;
}
else if (currentChar == '\b')
{
if (index > 0)
{
// Delete last character
printf("\b \b");
readCharacters[index] = '\0';
--index;
}
}
else
{
printf("%c", currentChar);
readCharacters[index] = currentChar;
++index;
}
}
if (index == 0)
{
// User pressed enter without having entered a number, let's give him a zero then
return 0;
}
readCharacters[index] = '\0';
int retVal = atoi(readCharacters);
// Worth noting that the value of converted user given string shouldn't be greater than what a signed int can hold
return retVal;
}
int main(int argc, char* argv[])
{
// Unlike scanf_s this will not generate a new line on enter
printf("getIntFromUser() sample (enter a number)\n");
int someValue = getIntFromUser();
printf(" -- This will be printed on the same line. (someValue is %d)\n\n", someValue);
// scanf_s sample
int anotherValue;
printf("scanf_s() sample (Insert a number.)\n");
scanf_s("%d", &anotherValue);
printf("This will be printed on a new line\n\n");
printf("Press any key to exit.");
_getch();
return 0;
}
EDIT
I feel like the above would become less readable if I were to add a comment over every code line. Instead I'm going to paste some blocks of code 1 by 1.
But first about the _getch function: It waits for the user to type something into the console and then returns the user given char as an int. char implicitly converts to int, so you may compare the _getch result to a character as I did many times in getIntFromUser (e.g. if (currentChar == '\b') { ... }).
You should also know about the values a char can hold and what their values are as an int (check out http://en.cppreference.com/w/cpp/language/ascii).
Going by the table the char '0' would be value 48 as an int, which is what _getch would return if the user were to type a 0.
First declare an array/string of 10 elements. Hope you know about them already. In this case the array is basically a chain of 10 elements that are all of type char, which are also referred to as string.
char readCharacters[10];
An indexer for the string is required.
int index = 0;
Below we have the usual for loop that...
1st: creates a variable of type int and assigns the result of _getch to it.
2nd: will determine if the loop shall keep executing. In this case the loop will break when currentChar is not '\r', which is an escape sequence that represents enter as a character.
3rd: will execute stuff inside once and then update currentChar with a new _getch.
for (int currentChar = _getch(); currentChar != '\r'; currentChar = _getch())
Checks if the user input (retrieved via _getch) is smaller than '0' (value 48 as an int) and greater than '9' (value 57 as an int). If either of them is true it will additionally check if the value of currentChar is not '\b' (value 8 as an int), which is the escape sequence for a backslash.
When that additional check evaluated to true as well then the keyword continue is used. Meaning that the rest of the block in the loop is not executed and instead the loop will start at the top again by getting a new user input and evaluating if the loop is to be continued by checking if obtained currentChar was enter.
if ((currentChar < '0' || currentChar > '9') && currentChar != '\b')
{
continue;
}
NOTE: You might want to read the comments on the else statement before you read these.
When the above if statement was false we get to the next if-statement (actually else if) that we see below.
As mentioned above: '\b' is backslash and if this is the user given char as well as string/array index being greater than 0 we move one character backwards in the console by "printing" '\b' and then write an empty character in order to delete what was written at that place previously. That puts us back to the position we were before so we print another backslash. At this point you might wonder why not just go back to the previous line that scanf_s causes, but that won't work. We must also not forget to replace the last string character with a null terminator and then set the index back by 1.
else if (currentChar == '\b')
{
if (index > 0)
{
// Delete last character
printf("\b \b");
readCharacters[index] = '\0';
--index;
}
}
When we hit this point we know that currentChar is something between 48 and 57 ('0' and '9').
_getch told the program what the user's input was, but we cannot see it in the console unless we print it there. So let's do that.
Also append the user's given character to the string as well as incrementing the index by 1.
else
{
printf("%c", currentChar);
readCharacters[index] = currentChar;
++index;
}
Lastly we call the atoi function that will convert our string/array to an integer.
int retVal = atoi(readCharacters);

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.

Resources