I want to write code that checks if the user enters correct input, i.e 1, 2, 3 or 4. Otherwise, the message "input error" is printed. If the user enters a letter for example, since the input variable in the scanf is char type, it works too.
But in the case of multiple characters, I throught about the following solution: I try to enter all the characters into a char array and to check how many members into it. I wrote the following code:
char option;
int countIn;
char inArray[10];
do { //while option!=4
scanf("%c", &option);
while (countIn < 10 && scanf("%c", &option) != -1 && option != '\n') {
inArray[countIn] = option;
countIn++;
}
if (countIn > 1) { option = 10; }
else { option = inArray[0]; }
countIn = 0;
} while (option != '4');
The problem is when I enter 1 for example, the program works well, but for the second loop iteration, the scanf doesn't work and the program does automatically the part 1 again and again.
what did I do wrong?
Replace both scanf() calls with:
scanf(" %c", &option);
Note the space in the format string, which tells scanf to consume all the whitespaces. The reason why it seems to skip is the newline left in the input buffer by previous input.
From scanf():
ยท A sequence of white-space characters (space, tab, newline,
etc.; see isspace(3)). This directive matches any amount of
white space, including none, in the input.
Note that even though EOF is typically defined as -1, it's not safe to assume so. I would strongly suggest to use EOF instead of -1.
Related
While executing the small piece of code below, every time I enter a character, the output is repeated and I don't understand why. Can someone explain to me why is it behaving like this?
ps: Just started my programming journey in c.
If I print a character such as 'a' I'm supposed to have 1 as output then another prompt asking me to enter a character again. But I instead get the 1, a prompt, and a 1 again then another prompt asking me to enter a character.
#include <stdio.h>
int main()
{
int usr_ch;
for ( usr_ch = 0; (usr_ch != 'q');){
printf("Enter a single character: (enter 'q' to stop)\n");
usr_ch = getc(stdin);
printf("%d\n", (usr_ch != 'q') ? 1 : 0));
}
return 0;
}
input: u
output:
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)
You already have a great answer explaining the additional '\n' character generated when the user presses ENTER. Continuing from the comments below the question and comment by #AllanWard about the use of fgets(), it can provide the ability to take all single characters as input and end the input when ENTER alone is pressed. There are a number of other benefits as well.
When reading a line with fgets() you read the input into a buffer (character array or allocated block of memory). Don't skimp on buffer size... fgets() reads and includes the trailing '\n' in the buffer it fills. This means an entire line of input is consumed, including the trailing '\n' given a sufficiently sized buffer. The '\n' is not left in the input buffer (stdin) unread. This will avoid the problem you are experiencing.
To access the first character in the array, all you need do is derefernce the pointer. (an array is converted to a pointer to its first element on access, C18 Standard - 6.3.2.1(p3)). So if you declare char line[1024]; to hold the input, simply referencing *line provides access to the first character.
Using fgets() avoids all of the pitfalls new C programmers fall into using scanf() and eliminates the '\n' being left unread. These are the primary reasons new C programmers (as well as not so new C programmers) are encouraged to take all user input using fgets() (or POSIX getline() which behaves in the same manner, but can also provide auto-allocation to handle a string of any length)
In addition to taking the input, without much more effort you can ensure the user has only entered one-printable character with a few simple tests. This allows you to handle individual error cases as needed. A short example of the use of fgets() and handling several of the foreseeable error cases can be written as:
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void)
{
char line[MAXC]; /* buffer to hold line */
/* prompt and then read input into line */
while (fputs ("Enter a single character: (enter alone to stop): ", stdout) &&
fgets (line, MAXC, stdin)) {
/* if ENTER alone, break */
if (*line == '\n') {
puts ("exiting");
break;
}
/* if not a single-character, handle error */
else if (line[1] != '\n') {
fputs (" error: more than 1 char entered.\n", stderr);
}
/* if printable character, output */
else if (isprint ((unsigned char)*line)) {
printf (" you entered '%c'\n", *line);
}
else { /* otherwise, handle error */
fputs (" error: non-printable character generated.\n", stderr);
}
}
}
(note: these are only a few examples of the classification test you can use. You are free to add or remove as many as you like. You can even provide a lookup-table for non-printable character and output a representation, e.g. '\t', when one is pressed, it's entirely up to you.)
Example Use/Output
The following exercises each of the covered error cases (the '\t' character is used for the non-printable character), e.g.
$ ./bin/fgets-char
Enter a single character: (enter alone to stop): a
you entered 'a'
Enter a single character: (enter alone to stop): b
you entered 'b'
Enter a single character: (enter alone to stop): q
you entered 'q'
Enter a single character: (enter alone to stop): Q
you entered 'Q'
Enter a single character: (enter alone to stop):
error: non-printable character generated.
Enter a single character: (enter alone to stop): foo
error: more than 1 char entered.
Enter a single character: (enter alone to stop):
exiting
There is absolutely nothing wrong with using getc() or fgetc() or getchar() for taking a single-character as input, but you must handle any additional characters that remain unread (including the trailing '\n'). Then what if the user presses ENTER twice in a row, or a cat steps on the keyboard generating 500 keypresses? That's where fgets() can help.
Another approach, unfortunately non-portable between different OS's, is to place the terminal in raw unbuffered (non-cannonical) mode where the input is processed immediately. For Linux you can use tcsetattr(). (you can also use setvbuf, see man 3 setbuf to switch between unbuffered, line-buffered or fully-buffered input) For Windows getch() can be used.
Worth exploring each as you continue your learning in C. Let me know if you have further questions.
stdin, by default, is line oriented when you enter "u\n" so getc() will first return u and in the next call \n.
In this case it is more natural to use a do-while-loop (or a for(;;) with a break at the end of the loop body). Then read a letter in a loop till you find one you like (i.e. not a \n). It's a good idea to handle EOF.
#include <stdio.h>
int main() {
int usr_ch;
do {
printf("Enter a single character: (enter 'q' to stop)\n");
while((usr_ch = getc(stdin)) && usr_ch == '\n');
printf("%d\n", usr_ch != 'q'));
} while(usr_ch != 'q' && usr_ch != EOF);
}
and here is example runs:
Enter a single character: (enter 'q' to stop)
a
1
Enter a single character: (enter 'q' to stop)
q
0
AYou can also just fgets() a line or use scanf(" %c", &usr_ch) to ignore leading white space.
I have wrote a small code to get value from Fahrenheit to Celsius. I wanted to keep inputting data until I press any other key than 'y'. But this loop doesn't work that way and stops after one iteration.
#include <stdio.h>
int main()
{
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
printf("ch=%c",ch);
scanf("%c",&ch);
}while(ch == 'y');
return 0;
}
What is the problem here?
P.S
I added a line and made a new code like this
#include <stdio.h>
int main()
{
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);//here we press carriage return. this value is in stdin
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
scanf("%c",&ch);//putting a space before %c makes the newline to be consumed and now it will work well
if((ch == '\r')|| (ch == '\n'))
printf("1\n");
printf("ch=%c",ch);//this takes the carriage return in stdin buffer
}while(ch == 'y');
return 0;
}
I need to know carriage return here is \r or \n?
When the value for scanf("%d",&far); is entered and press enter, the scanf stores the carriage return in the buffer. When it encounters the second scanf in the code scanf("%c",&ch); it takes the carriage return present in the buffer as the input to 'ch'. So it doesn't wait for the user input.
Please have a look at the post here
As indicated in one of the reply the solution is to put a space in scanf
scanf(" %c",&ch);
You should always check the return value of scanf. Your first use of scanf may fail if the user does not enter a valid integer, in which case, you are using far without initialising it (which is undefined behaviour). scanf returns the number of items that were successfully scanned. If you are requesting scanf to scan one integer, then it should return 1 if it successfully managed to scan an integer.
int scanresult = scanf("%d", &far);
if (scanresult != 1)
{
puts("Invalid input or unexpected end of input");
return 1;
}
In addition, the %c conversion specifier is unique in that it does not cause scanf to gobble up any preceding whitespace unlike the other conversion specifiers. To force scanf to gobble up the whitespace (such as linefeeds, carriage returns, spaces, tabs etc), simply put a space character before the %c, e.g.
scanresult = scanf(" %c", &ch);
For scanf, the space character is actually a directive to parse and skip all whitespace.
This is because of the previous newline character remaining in the buffer. You can simply replace scanf by this line:
while((ch = getchar()) == '\n');
You'll be needing the same technique in combination with ungetc() in many occasions.
Add fflush() function, just above scanf("%c", &ch). Because buffer of CONSOLE INPUT stores characters that not returned to program. Which is ENTER pressed in previous scanf:
#include <stdio.h>
int main() {
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
printf("ch=%c",ch);
scanf("%c",&ch); // This scanf will be ignored, because loads last
// character from buffer that can be recognized
// by scanf which is pressed "ENTER" from previous scanf
printf("%d", ch) // Shows 10, which is ASCII code of newline
fflush(stdin); // Clear buffer
scanf("%c",&ch); // Now it will prompt you to type your character.
// printf("%c"ch); //Without fflush, it must show 10, which is \n code
}while(ch == 'y');
return 0;
}
if after Y you press "space" or "return" this is the character you will find in %C
I have a project that must be made only in C (I'm not allowed to use C++).
Right now I'm working on the UI (a little menu showed in the console, each option has a number from 0 to 6 assigned to it, the user typed the number, hits enter and so on).
I'm having a problem with the reading option function.
At first I tried
int option;
scanf("%d", &option);
but this caused problems when I typed in characters.
I tried reading a char:
char option;
scanf("%s", &option);
option -= '0';
This allowed me to treat it like a number and worked nice for the first tests, allowing me to verify if the option is valid (it's not a letter, it's a number between 0 and 6).
The problem is that I can type more than one character and all of them will be stored somewhere in memory. And that's obviously bad.
I tried reading with "%c", but that will display the error message for every character in the string I entered.
To make it a bit more clear, this is the function
int readOption(int maxOp)
{
char option = -1;
while(option < 0 || option > maxOp)
{
scanf("%c", &option);
option -= '0';
if(option < 0 || option > maxOp)
printf("Invalid option!\nTry again: \n");
}
return option;
}
If I type "abc", the error message will be printed 3 times.
So how can I make sure that any extra characters entered are ignored?
Try this
int ch;
scanf(" %c", &option);
while((ch = getchar()) != '\n');
Untested, buy you may try something like this:
scanf(" %[0-6]1d*[^\n]", &option);
The call to scanf will only be valid if the input is a single number between 0 and 6, ignoring leading spaces. * suppresses any character after that isn't in this range and that is not a newline.
You can use getc() to do the job like this:
int main(){
char choice = getc(stdin);
fflush(stdin);
}
Now choice is the first entered character, everything entered after the first character is deleted so it won't interrupt any other user inputs.
In general, when the user enters input it's stored in a special buffer called stdin, if you don't delete that buffer after reading the first character using fflush(stdin) it will interrupt any future user inputs.
I'm writing a C program that needs to accept user input of up to 100 characters, but the user is allowed to input less than that limit. I'm trying to implement this idea with a while loop that continues to accept char input until the user presses enter (ascii value of 13), at which point the loop should break. This is what I've written:
char userText[100]; //pointer to the first char of the 100
int count = 0; //used to make sure the user doens't input more than 100 characters
while(count<100 && userText[count]!=13){ //13 is the ascii value of the return key
scanf("%c", &userText[count]);
count++;
}
Launching from the command line, if I enter a few characters and then press enter, the prompt simply goes to a new line and continues to accept input. I think the problem lies with my lack of understanding how scanf receives input, but I'm unsure of how to change it. What can I do to make to loop break when the user presses enter?
Because you read into &userText[count] and then do count++, so you loop condition userText[count]!=13 is using the new value of count. You can fix it with:
scanf("%c", &userText[count]);
while(count<100 && userText[count]!='\n'){
count++;
scanf("%c", &userText[count]);
}
And as Juri Robl and BLUEPIXY are pointing out, '\n' is 10. 13 is '\r', which isn't what you want (most likely).
while(count<100 && scanf("%c", &userText[count]) == 1 && userText[count]!='\n'){
count++;
}
You should probably check against \n (=10) not 13. Also you check against the wrong count, it is already one to high.
int check;
do {
check = scanf("%c", &userText[count]);
count++;
} while(count<100 && userText[count-1]!='\n' && check == 1);
userText[count] = 0; // So it's a terminated string
On the other hand you could use scanf("%99s", userText); which allows up to 99 chars input (and one at the end for the 0).
The check for check == 1 looks for an error in reading, for example an EOF.
In my task I need to use a loop and get an input between 1-5, if i get any other input i need to keep iterating until i get 1-5.
Could you please tell me what am i doing wrong?
Part of my code:
int rateSelected, weeklyHours;
printf("Enter the number corresponding to the desired pay rate or action:\n");
printf("1) %.2lf$/hr 2) %.2lf$/hr\n", RATE1, RATE2);
printf("3) %.2lf$/hr 4) %.2lf$/hr\n", RATE3, RATE4);
printf("5) Quit\n");
while ((scanf("%d", &rateSelected)) != EOF && rateSelected != 5)
{
if (rateSelected > 5 || isalpha(rateSelected) ==1){
printf("please enter a number between 1-5:\n");
continue;
}
printf("Now enter your weekly hours:\n");
scanf("%d", &weeklyHours);
ChoosePayRate(rateSelected, weeklyHours);
}
tnx
The problem is your use of %d format specifier. When letters are entered instead of digits, scanf returns zero to indicate that nothing is read. If you would like to allow entering letters along with digits, you should either add a read of a string when scanf returns zero, or always read into a string buffer, and then use sscanf or atoi to convert the string to integer.
You better use fgets() and strtol() for this. Scanf and the line-buffering of stdio is not very helpful together...
char line[LINE_MAX];
do {
fgets(line, sizeof(line), stdin);
} while(!isdigit(line[0]));
int choice = strtol(line, NULL, 10);
isalpha(rateselected) will never be true because you are storing an int in rateselected.
scanf("%d",rateselected) allready takes care of catching character input, and returns 0 if that is the case. So you should change the isalpha test to a rateselected == 0 test.
Also, scanf will never return EOF. It will return 0, and then you need to test feof(stdin) to see if you really hit the end of input. (which would correspond to a ctrl-Z for keyboard input).
Remove the isalpha(rateSelected).
isalpha() checks if the value passed as parameter is an alphanumeric character - but you are passing the int value which you have just read.
However, this is still not sufficient - you would need to catch the return value from scanf() to check if scanf() has actually read an int. But if no int was entered, the characters are not discarded so that the next scanf() will again try to convert them, which leads to an endless loop.
Better use the solution provided by #dasblinkenlight.
Use this:
int e;
while ((e = scanf("%d", &rateSelected)) != EOF)
{
scanf("%*[^\n]"); // this clean your input buffer
if (e==0 || rateSelected>5 || rateSelected<1) {
printf("please enter a number between 1-5:\n");
continue;
}
instead of
while ((scanf("%d", &rateSelected)) != EOF && rateSelected != 5)
{
if (rateSelected > 5 || isalpha(rateSelected) ==1){
printf("please enter a number between 1-5:\n");
continue;
}