scanf read variable number of characters - c

I was wondering how I can get scanf to skip reading a character if I press enter... My code is the following:
#include <stdio.h>
int main(void)
{
int a, status;
char b;
printf("Please enter a positive number immediately"
"followed by at most one lower-case letter:\n\n");
status = scanf("%i%c", &a, &b);
if (status == 1 && getchar() == '\n') {
printf("\nThank you!\n");
}
return 0;
}
When I enter just a number and nothing else, I need to press enter again to trigger the %c, &b in scanf. How do I avoid that and get the program to accept just 1 number to jump to the printf?
I tried:
if (status == 1 && getchar() == '\n')
but that won't work.

As noted in the comments, your best course of action is to use fgets to just read in a string, then parse and validate it. This Thread will provide you with enough resources for you to educate yourself about the use of fgets.
Here is one approach you can take. Please note that this code does not attempt to validate every possible input a user can provide, but rather give you a reasonable direction you can take to solve your problem if the input is assumed correct. I will leave the task of validation to you. The code below should provide enough tools to accomplish the rest of your task. Look at using a for loop to step through the buffer and ensure the input is correct. Use isalpha() and isdigit() to test each character. You can also implement your own functions to test each character as is done
in this answer.
#include <stdio.h>
#include <stdlib.h> //for atoi()
#include <string.h> //for strlen()
#include <ctype.h> //for isalpha()
#define MAX_INPUTLENGTH 500
int main(void)
{
//Always a good idea to initialize variables to avoid Undefined Behaviour!
char buffer[MAX_INPUTLENGTH] = { '\0' };
int a = 0, status = 1, length = 0;
char b = '\0';
printf("Please enter a positive number immediately"
"followed by at most one lower-case letter:\n\n");
//this gets you a string you can work with
fgets(buffer, sizeof(buffer), stdin);
length = strlen(buffer);
buffer[length - 1] = '\0';//remove the trailing '\n'
length--;
//now see if last character is a letter
if (isalpha(buffer[length - 1])) {
b = buffer[length - 1];//then assign and..
buffer[length - 1] = '\0';//trim the letter
}
//this function converts the remaining string to an int
a = atoi(buffer);
//Use the debugger and observe how these functions work in order
//to validate the input. for now, status is always 1!
if (status == 1) {
printf("\nThank you!\n");
}
return 0;
}
As noted in the comments below by #Jonathan, to portably get the count of an array, one should use sizeof(buffer) / sizeof(buffer[0]). Since you are using a char[], sizeof(buffer[0]) evaluates to 1, thus can be omitted when calling fgets.

Related

Why does scanf enter a loop when I input a character?

I have to finnish a college project, and a part of my code is acting strangely.
The goal of that part is to get an user input of an integer and store it in a variable so that i can use it later, however if the user inputs a character I have to ask for the number again.
I used the scanf function to get the user input and put it inside a while loop to continuously ask for the input in case it's invalid.
The problem is that when a user inputs a character, the code freaks out and starts running the while loop without stopping in the scanf to get the user input.
It makes sense that the loop condition is always true but the strange part is that it doesn't stop to read new inputs.
I deconstructed my code in order to replicate the problem to make it easier to debug.
I know that there are some useless variables but in my original code they are useful, I just kept them there to make it look similar to the original.
I can only use scanf to get user input, despite knowing them, in this project I am only allowed to use scanf. I can't use scanf's format to get characters, only numerical types are allowed in this project.
C11 is the version of the standart we are using in classes.
I'm sory if the solution for this is a dumb thing, I'm not good at C and I'm having some difficultlies this semester...
Thanks in advance.
while (!verification) {
printf(">>>"); //write values in here
check = scanf("\n%d", &var); //input a number and store the number of valid inputs
if (check) verification = 1; //if the input is a number then the while condition should turn to false with this statement
printf("var = %d, check = %d, verification = %d\n", var, check, verification); //printing all variables
}
If the user does not input an integer there are characters left in the input stream after the call to scanf. Therefor you need to read to end of line before making the next attempt to read an integer. Otherwise scanf will try to read the same non-integer characters again and again. Here is an example:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch, i, n;
n = scanf("%d", &i);
while (n == 0) {
fprintf(stderr, "wrong input, integer expected\n");
do {
ch = getchar();
} while ((ch != EOF) && (ch != '\n'));
n = scanf("%d", &i);
}
if (n == 1) {
printf("%d\n", i);
} else { /*n == EOF*/
fprintf(stderr, "reading input failed\n");
exit(EXIT_FAILURE);
}
return 0;
}
Don't use scanf() to read input from the user.
It's really only meant for reading data that's known to be in a particular format, and input from a user... often isn't.
While you do correctly check the return value of scanf("%d"), and could fix the case where the input isn't a number, you'll still have problems if the input is either an empty line, or a number followed by something else (123 foobar).
In the case of an empty line scanf() will continue waiting for non-whitespace characters. This is probably confusing, since users will expect hitting enter to do something.
In the case there's trailing stuff after the number, that stuff stays in the input buffer, and the next time you read something, it gets read. This is again probably confusing, since users seldom expect their input to one question to also act as input to another.
Instead, read a full line with fgets() or getline(), then run sscanf() or strtol() on that. This is much more intuitive, and avoids the disconnect caused by scanf() consuming input lines only partially (or consuming more than one line). See also e.g. scanf() leaves the new line char in the buffer
Here, using getline() (POSIX, even if not in standard C. Use fgets() instead if getline() is not available):
#include <stdio.h>
int main(void)
{
char *line = NULL;
size_t len = 0;
int result;
printf("Please enter a number: ");
while (1) {
if (getline(&line, &len, stdin) == -1) {
/* eof or error, do whatever is sensible in your case */
return 1;
}
if (sscanf(line, "%d", &result) != 1) {
printf("That didn't seem like number, please try again: ");
continue;
}
break;
}
printf("You entered the number %d\n", result);
}
The problem is you must discard offending input when the conversion fails.
Here is a simple solution using only scanf() as instructed:
#include <stdio.h>
int main() {
int n;
for (;;) {
printf("Enter an number: ");
switch (scanf("%d", &n)) {
case 1:
/* successful conversion */
printf("The number is %d\n", n);
return 0;
case 0:
/* conversion failure: discard the rest of the line */
scanf("*[^\n]"); // discard characters before the newline if any
scanf("*1[\n]"); // optional: discard the newline if present
printf("Invalid input. Try again\n");
continue;
case EOF:
/* input failure */
printf("Premature end of file\n");
return 1;
}
}
}

scanf test failing inside a function in C

I'm trying to do a program with a simple game for a user to guess the number. My code is below:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 30
#define TRYING 5
void guessnumber(int, int, int *);
int main(void) {
int mytry = 1;
guessnumber(MAX, TRYING, &mytry);
if (mytry <= TRYING)
printf("Congratulations! You got it right in %d tries\n", mytry);
else
printf("Unfortunately you could not guess the number in the number of tries predefined\n");
printf("End\n");
return EXIT_SUCCESS;
}
void guessnumber(int _n, int _m, int *_mytry) {
srandom(time(NULL));
int generated = 0, mynum = 0, test = 0;
generated = rand() % (_n + 1);
printf("Welcome to \"Guess the number\" \n");
printf("A number between 0 and %d was generated\n", _n);
printf("Guess the number:\n");
while (*_mytry <= TRYING) {
test = scanf(" %d", &mynum);
if (test != 1 || mynum < 0 || mynum > MAX)
printf("ERROR: please enter a valid number \n");
else
if (mynum > generated)
printf("Wrong! The number your trying to guess is smaller\n");
else
if (mynum < generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry = *_mytry + 1;
}
}
Okay, now the program is working pretty ok except for one thing: the scanf test.
It works if I try to enter a number out of my range (negative or above my upper limit) but it fails if I for example try to enter a letter. What it does is that it prints the message of error _m times and then it prints "Unfortunately you could not guess the number in the number of tries predefined" and "End".
What am I doing wrong and how can I fix this?
In case, a character is entered, you're trying to detect it correctly
if(test!=1 ......
but you took no action to correct it.
To elaborate, once a character is inputted, it causes a matching failure. So the input is not consumed and the loop falls back to the genesis position, only the loop counter is increased. Now, the previous input being unconsumed, is fed again to the scanf() causing failure once again.
This way, the loop continues, until the loop condition is false. Also, for every hit to scanf(), as unconsumed data is already present in the input buffer, no new prompt is given.
Solution: You need to clean the input buffer of existing contents when you face a failure. You can do something like
while ((c = getchar()) != '\n' && c != EOF);
to clean the buffer off existing contents.
When you enter a letter, scanf() leaves the letter in the input stream since it does not match the %d conversion specifier. The simplest thing to do is use getchar() to remove the unwanted character:
if (test != 1) {
getchar();
}
A better solution would be to use fgets() to get a line of input, and sscanf() to parse the input:
char buffer[100];
while (*_mytry<=TRYING)
{
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()");
exit(EXIT_FAILURE);
}
test=sscanf(buffer, "%d", &mynum);
if(test!=1 || mynum<0 || mynum>MAX)
printf ("ERROR: please enter a valid number \n");
else if(mynum>generated)
printf("Wrong! The number your trying to guess is smaller\n");
else if(mynum<generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry=*_mytry+1;
}
In the above code, note that the leading space has been removed from the format string. A leading space in a format string causes scanf() to skip leading whitespaces, including newlines. This is useful when the first conversion specifier is %c, for example, because any previous input may have left a newline behind. But, the %d conversion specifier (and most other conversion specifiers) already skips leading whitespace, so it is not needed here.
Additionally, your code has srandom() instead of srand(); and the call to srand() should be made only once, and probably should be at the beginning of main(). And, identifiers with leading underscores are reserved in C, so you should change the names _m, _n, and _mytry.

Reading until I manage to enter an integer

I'm pretty new in C, I used to work in Python, and I'm trying to see if something that I read is integer number. If not, to read until I manage to entry a number.
I did some research and I found out that the function scanf actually returns 1 if the read is done suitably, and 0 otherwise.
So, I have written this code, and I don't understand why this is an infinite loop, writing "Give an integer" on the console
#include <stdio.h>
int main() {
int a;
int b = 1;
do {
printf("Give an integer\n");
b = scanf("%d", &a);
} while (b == 0);
}
The problem with scanf() is that it stops reading when the first white space is encountered for most specifiers like "%d", so it's left in the input and that's why reading again would cause a problem if you don't discard such white space because it will then return immediately the next time you call it. There is a mandatory white space that is introduced when you press Enter or Return, the '\n' new line character (or line feed).
If you want to read an integer and make sure you did you can try like this
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int
main(void)
{
long int value; // You can adapt this to use `int'
// but be careful with overflow
char line[100];
while (fgets(line, sizeof(line), stdin) != NULL) {
char *endptr;
value = strtol(line, &endptr, 10);
if ((isspace(*endptr) != 0) && (endptr != line))
break;
}
fprintf(stdout, "%ld\n", value);
return 0;
}
read strtol()'s manual to understand this code
You could as suggested in the comments, remove the white space characters from the input, but IMHO that is harder and you would still have other problems with scanf(), like handing empty input which is not straight forward.
I don t understand why this is an infinite loop, writing "Give an intiger" on the console
The problem is that scanf() does not consume data that it cannot match against the specified format. It leaves such characters unread in the input stream. Therefore, if you try reading again from the same stream with the same format, without consuming at least one character by some other means, then you can be certain that the input will again not be matched. And again. And again.
To avoid your infinite loop, you need to consume at least one character of the non-matching input after each matching failure. There are many ways you could do that; here's a fairly simple one:
#include <stdio.h>
int main() {
int a;
do {
printf("Give an intiger\n");
if (scanf("%d", &a)) {
// breaks from the loop on a successful match or an error
break;
}
// consume the remainder of one line of input without storing it
if (scanf("%*[^\n]") == EOF) {
break;
}
} while (1);
}
That consumes the whole remainder of the line on which the non-matching input is encountered, which will yield less surprising interactive behavior for some inputs than many alternatives would.
If you've a penchant for writing terse code, or if you don't like to break out of the middle of a loop, then you might write the same thing like this:
#include <stdio.h>
int main() {
int a;
do {
printf("Give an intiger\n");
} while ((scanf("%d", &a) == 0) && (scanf("%*[^\n]") != EOF));
}
Because the && operator short circuits, the second scanf() call will be executed only if the first returns zero, and the loop will exit after the first iteration wherein either the first scanf() call returns nonzero or the second returns EOF (indicating an error).

Why is my C code skipping the next user input?

I'm trying to convert my Caesar Cipher code from taking a user given argument to using a user given input, but it's not going the way I've intended at all. I have this code, and it asks the first input for the ROT number, but then it skips the input for the rest of the code. Now, if I wanted to rotate by 2 and use the string bB, the output should be dD, and it is, but only if, when aksed for the input, you put "2 bB". I don't know why this is, and I've looked at other threads saying to just put scanf("%c", &blah);, but I don't know how to do this in my situation. Any help is thankful.
Edit: Changed char to int, as I did in my code just before I posted this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(){
/**********************************************************************************************************************************/
int bytesRead;
int nbytes = 255;
char *encryptString;
encryptString = (char *) malloc (nbytes + 1);
//char encryptString[256];
char finalChar;
char finalString[256];
int rotNum;
/**********************************************************************************************************************************/
puts("Please enter the ROT (rotate) number you wish to encrypt by: ");
scanf("%d", &rotNum);
printf("Please enter the phrase you'd like to encrypt: \n");
fgets(encryptString, sizeof(encryptString), stdin);
printf("The string entered is: %s\n", encryptString);
printf("The encrypted version is: ");
int n = strlen(encryptString) - 1;
int i;
for(i = 0; i < n; i++){ //For loop to go through the entire string entered
if(isupper(encryptString[i])){
finalChar = (((encryptString[i] - 65) + rotNum) % 26) + 65;
finalString[i] = toupper(finalChar);
//printf("%c\n", finalChar);
}
else if(islower(encryptString[i])){
finalChar = (((encryptString[i] - 97) + rotNum) % 26) + 97;
finalString[i] = tolower(finalChar);
//printf("%c\n", finalChar);
}
else{
finalChar = ' ';
finalString[i] = finalChar;
}
printf("%c", finalString[i]);
}
printf("\n");
return 0;
}
You have few problems in your code:
1)
scanf("%d", &rotNum);
Here you are passing a char * to scanf(). Declare rotNum as int.
2) After reading the input rotNum, scanf() leaves a '\n' in the input buffer.fgets(); stops reading input once encounters a \n. So fgets() doesn't read at all. Use getchar(); after scanf() call to consume the newline char. Or better, read the rotNum using fgets() and parse it using sscanf().
3) Your second argument to fgets() is wrong.
fgets(encryptString, sizeof(encryptString), stdin);
Here, encryptString is a pointer. So this will give you the size of pointer on your platform, not the number of bytes (256) that it points to. Change it to:
fgets(encryptString, 256, stdin); // or whatever the bytes you allocate
Additioanlly,
1) Use a proper return type for main() such as int main(void) or int main(int argc, char **argv) or equivalent.
2) Check the return value of malloc() for NULL to see if it's failed.
3) Casting the malloc() return is unnecessary and error-prone.
I made three changes to your code and it worked without any problem:
1. Use "int rotNum" instead of "char rotNum"
2. Use "scanf("%s", encryptString)" instead of "fgets(encryptString, sizeof(encryptString), stdin)"
3. Either use "int n = strlen(encryptString)" or "for(i = 0; i <= n; i++)"
You have to make some other changes to make it work for negative rotNum.
as the man page of fgets says ,
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline.
So when you enter rotate number & then hit enter,the input buffer will contain number\n.
While number will be stored in rotNum , \n will remain in stdin.
So fgets will read it & returns without waiting for input.
Use scanf instead of fgets.
If you are using linux machine, here is the man page
Try fflush(stdin) before using fgets. This should clear the \n from stdin

stopping `scanf` when user enters "." DOT

I am messing around with the function below, I want to end input capture when user enters a DOT character. It seems that getche() is not doing what it is intentended to do:
void Encode(FILE *fp)
{
char chWord[100];
char *chP;
printf("Enter a word or a sentence, close it by a \".\"\r\n");
scanf("%s",chWord);
if (chWord != '.')
{
for (chP = chWord; *chP != '\0'; chP++) //to print each digit till end of string \0
{
printf("%d ",*chP+10);
fprintf(fp, "%d ",*chP+10);
}
}
}
UPDATE
It seems that I was not clear enough. What I am trying to do is when user enters a DOT it should act like pressing ENTER key so the program goes to next step. Some sort of simulating ENTER key.
if (chWord != '.')
should be
if (*chWord != '.')
you are comparing a char pointer to a char instead of a char to another char.
be aware that the way this code is written the input ".123" will skip the printing segment. not sure if this is desireable to you or not.
The scanf family of function accept a (negative)character set as a format specifier.
You can do scanf("%[abc]", chWord); to accept only strings composed of the letters abc.
And you can also specify which characters not to accept. So scanf ("%[^.]", chWord); will accept a string composed of anything but a dot.
Edit
I forgot to mention, that the dot will remain in the input stream buffer, so to read and ignore it during the scanf itself (rather than flush the buffer or do a getchar), just add it to the end of the format string. I.e.:
scanf ("%[^.].", chWord);
OK, backing out that whole Answer based on your update...
The answer is no, there is no way to do what you want to do with scanf, or anything in standard C for that matter. What you're trying to do is platform (and possibly compiler) specific.
If you want to treat the '.' as a enter key press you have to do the magic yourself. So, since you didn't mention if you were using any specific OS or compiler I'll give you the first example that comes to mind.
This works with Windows MS VS:
#include <Windows.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char key = 0;
int counter = 0;
char chWord[100] = {0};
while(counter < 100) {
while(!_kbhit()) { //While no key has been hit
Sleep(1); //Sleep for 1 ms
}
key = _getch(); //Get the value of the key that was hit
if(key == '.') //if it was a .
break; //act as if it were an "enter" key and leave
else
chWord[counter] = key;
counter++;
}
chWord[99] = '\0';
printf("The string was %s\n", chWord);
return 0;
}

Resources