Discarding characters in C? - c

I'm doing intro to C and I need to write a program that prompts the user to enter in characters, an equal sign, and an integer. I need to use getchar() until the '=' then scanf() for the integer. The program should then output only the integer back to the user.
Right now it prints out unnecessary code re the location for every character input, then at the end outputs the correct integer. This is the code I have:
#include <stdio.h>
#define EQUAL '='
int main(void)
{
char ch;
int integer = 0;
printf("Enter some text, an equal sign and an integer:\n");
while ((ch = getchar())!= '\n') //while not end of line
{
if (ch == EQUAL){
scanf("%d", &integer);
}
printf("The integer you entered is: %d\n", integer);
}
return 0;
}
I can't find an example and need clarification on how to fix the issue.

You got bit by a gotcha in C. The problem is this:
if( ch == EQUAL )
scanf("%d", &integer);
printf("The integer you entered is %d\n", integer);
if with no braces will only include the single statement following it. That code is really this:
if( ch == EQUAL ) {
scanf("%d", &integer);
}
printf("The integer you entered is %d\n", integer);
To avoid this gotcha, I would recommend two things:
Always indent your code properly.
Never use if, else, or while without braces.
gcc supports a warning about this, -Wmisleading-indentation.
For more gotchas like this read "C Traps And Pitaflls" by Andrew Koenig.

Related

storing a negative integer in signed char

I'm trying to get a function that stores integers in char. I need to use char rather than int because I need to use the question mark (?) to terminate my loop. However, I can't seem to make my code work. Here's my work:
int main() {
signed char num;
scanf("%c", &num);
if (num!='?') {
printf("%c\n", num);
}
return 0;
}
When I input a negative number (say, -9), I get the output:
-
I tried using the integer print symbol (%d) rather than %c when I was printing the values, as I saw on this question: https://www.quora.com/Can-I-assign-a-negative-number-to-a-char-variable-Why-or-why-not but makes everything I input junky. ie when I input 2, it returns 50.
I was told that signed char should do the thing here, but I'm not sure that's the case now.
Thanks.
If you want to scan an integer and at the same time scan a character like ? you can do:
Try to scan an integer
If scan for integer fails, try to scan a char
Like
int num;
char c;
if (scanf("%d", &num) == 1)
{
// Okay you got an integer.
c = num; // BUT... watch out for over/underflow !!!
}
else if (scanf("%c", &c) == 1)
{
// Okay you got a char
if (c == '?')
{
// You got your ?
}
else
{
// Some other char
// Some kind of error handling... perhaps
c = 0;
}
}
else
{
// Input error that can't be recovered
exit(1);
}
See online example https://ideone.com/kTPE0M
You use the wrong format:
it has to be (but you will have to enter 63 instead of ?):
signed char num;
scanf("%hhd", &num);
or
char str[6];
char num;
fgets(str,5,stdin);
if (str[0]!='?') {
if(sscanf(str, "%hhd", &num) == 1)
printf("%c\n", num);
else {/* handle scanf error*/}
When you have to do anything at all complicated, scanf is almost never the right tool for the job.
And, although it might not be obvious at first, the task "read a thing that's either an integer, or a question mark that indicates the end of the input" is definitely, in this sense, something that's too complicated for scanf. There's no good format specifier for that kind of "thing".
The easiest thing is to read a line of text (using fgets, not scanf), then start trying to figure out what's in it — question mark, integer, or something else. (What if the user types "x"?)
#include <stdio.h>
int main()
{
char line[512];
int num; /* note int not char */
if(fgets(line, sizeof(line), stdin) == NULL)
printf("end of file or error\n");
else if(line[0] == '?')
printf("end of input\n");
else if(sscanf(line, "%d", &num) == 1)
printf("numeric input: %d\n", num);
else printf("unexpected input!\n");
}
Note that this program still isn't perfect: If the user, types, say, "123x", this program will say it saw numeric input, and won't notice or complain about the extra x. Also, it only looks at the first character of the line to see if it's a ?, so it will also accept things line ?? or ?Cat in the hat. (There are ways to fix both of those things, but they get rather elaborate, and are probably more trouble than they're worth for a simple exercise.)
See also What can I use for input conversion instead of scanf?

int validation using scanf and isdigit

I want to validate the input is int.
Ideally, I want it to prompt to the user if the validation failed.
I tried this:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int i;
printf("enter a number: ");
scanf("%i", &i);
(isdigit(i)) ? printf("%i is a digit\n", i) : printf("%i is NOT a digit\n", i);
}
it does not do what i want.
here is the result:
> enter a number: 1
> 1 is NOT a digit
but magically:
> enter a number: 54
> 54 is a digit
why? How can I do the validation in the right way?
== UPDATE ==
I tried some suggestion to not using isdigit but checking the scanf result, it works, as follow:
int i, checker;
printf("enter a number: ");
checker = scanf("%i", &i);
(checker) ? printf("%d Is digit\n", i) : printf("%d Is NOT digit\n", i);
here is the result
enter a number: 12
12 Is digit
enter a number: asdf
220151845 Is NOT digit
but it failed the while loop validation,
int i, checker;
do
{
printf("enter a number: ");
checker = scanf("%i", &i);
} while (!checker);
printf("number : %i\n", i);
especially when it is not a digit, then it become infinity looping the printf but not prompt to user at all.
Thanks.
Don't use isdigit() — it is for checking whether a character is a digit or not. You've read a number. You must check the return value from scanf() — if it is not 1, you've got a problem. Depending on your requirements, the fact that there may be all sorts of stuff on the line after the number may or may not be a problem. I'm assuming when you say "validate the input is an integer", you want to allow for multiple-digit numbers, and negative numbers, and since you used %i rather than %d, you're fine with octal values (leading 0) or hexadecimal values (leading 0x or 0X) being entered too.
Note that if you have:
int checker = scanf("%i", &i);
then the result could be 1, 0, or EOF. If the result is 1, then you got an integer after possible leading white space, including possibly multiple newlines. There could be all sorts of 'garbage' after the number and before the next newline. If the result is 0, then after skipping possible white space, including possibly multiple newlines, the first character that wasn't white space also wasn't part of an integer (or it might have been a sign not immediately followed by a digit). If the result is EOF, then end-of-file was detected after possibly reading white space, possibly including multiple newlines, but before anything other than white space was read.
To continue sensibly, you need to check that the value returned was 1. Even then, there could be problems if the value is out of the valid range for the int type.
The full requirement isn't completely clear yet. However, I'm going to assume that the user is required to enter a number on the first line of input, with possibly a sign (- or +), and possibly in octal (leading 0) or hexadecimal (leading 0x or 0X), and with at most white space after the number and before the newline. And that the value must be in the range INT_MIN .. INT_MAX? (The behaviour of scanf() on overflow is undefined — just to add to your woes.)
The correct tools to use for this are fgets() or POSIX
getline() to read the line, and
strtol() to convert to a number, and isspace() to validate the tail end of the line.
Note that using strtol() properly is quite tricky.
In the code below, note the cast to unsigned char in the call to isspace(). That ensures that a valid value is passed to the function, even if the plain char type is signed and the character entered by the user has the high bit set so the value would convert to a negative integer. The valid inputs for any of the ispqrst() or topqrst() functions are EOF or the range of unsigned char.
C11 §7.4 Character handling <ctype.h> ¶1
… In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.
The GNU C library tends to protect the careless, but you should not rely on being nannied by your standard C library.
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char line[4096]; /* Make it bigger if you like */
printf("Enter a number: ");
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "Unexpected EOF\n");
exit(EXIT_FAILURE);
}
errno = 0;
char *eon; /* End of number */
long result = strtol(line, &eon, 0);
if (eon == line)
{
fprintf(stderr, "What you entered is not a number: %s\n", line);
exit(EXIT_FAILURE);
}
if (sizeof(int) == sizeof(long))
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld: %s\n",
LONG_MIN, LONG_MAX, line);
exit(EXIT_FAILURE);
}
}
else
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld,\n"
"let alone in the range %d..%+d: %s\n",
LONG_MIN, LONG_MAX, INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
}
char c;
while ((c = *eon++) != '\0')
{
if (!isspace((unsigned char)c))
{
fprintf(stderr, "There is trailing information (%c) after the number: %s\n",
c, line);
exit(EXIT_FAILURE);
}
}
if (result < INT_MIN || result > INT_MAX)
{
fprintf(stderr, "What you entered is outside the range %d..%+d: %s\n",
INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
int i = result; /* No truncation given prior tests */
printf("%d is a valid int\n", i);
return(EXIT_SUCCESS);
}
That seems to work correctly for a fairly large collection of weird numeric and non-numeric inputs. The code handles both 32-bit systems where sizeof(long) == sizeof(int) and LP64 64-bit systems where sizeof(long) > sizeof(int) — you probably don't need that. You can legitimately decide not to detect all the separate conditions but to aggregate some of the errors into a smaller number of error messages.
isdigit() can only test one char if it's digit or not.
in order to test a multi-digit number, you must read this number char by char;
here is an example showing that:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int i=0;
char s[100];
printf("enter a number: ");
scanf("%99s", &s);
while(s[i]!='\0'){
if(!isdigit(s[i]))break;
i++;
}
if(s[i]=='\0') printf("%s is a number\n",s);
else printf("%s is NOT a number\n",s);
}
isdigit is a function to check a single char only.
It is not designed to check whether a string is a valid integer or not.
The argument "%i" reads input line and convert it to an integer, so the data stored in i is an integer number, not the ASCII code that isdigit expected.
If you want to validate a single character, you can use "%c" instead of "%i"
#include
#include
int main(){
char c;
int n = scanf("%c",&c);
if(isdigit(c)){
printf("%c is digit\n",c);
}else{
printf("%c is not digit\n",c);
}
return 0;
}
If you want to validate a line, you can check this:
[Check if input is integer type in C] (Check if input is integer type in C)
In short, use "%i%c" instead of "%i", check the result of "%c" to validate if input is purely in digits.
About a while loop with scanf not working, read this from the scanf Linux man page:
The format string consists of a sequence of
directives which describe how to process the sequence of input
characters.
If processing of a directive fails, no further input is read, and scanf() returns. A "failure" can be either of the
following: input failure, meaning that input characters were unavailable, or matching failure, meaning that the input
was inappropriate (see below).
Especially note "If processing of a directive fails, no further input is read"
So if your scanf fails to read an integer you need to do something to read and discard the bad input.

Having Difficulty with isdigit() in C

I have been trying to add some experience in C to my experience with Python and started with a basic addition program. One thing that I'm trying to do is check if the input is a number or a character as seen here:
#include <stdio.h>
#include <ctype.h>
int main()
{
int n, sum=0,c,value;
printf("Enter the Number of Integers You Want to Add\n");
scanf("%d", &n);
if(isdigit(n))
{
printf("Enter %d Integers\n", n);
for(c=1; c<=n; c++)
{
scanf("%d", &value);
if(isalpha(value))
{
printf("ENTER INTEGER NOT CHARACTER\n");
break;
}
else
{
sum = sum + value;
}
}
printf("Sum of Entered Integers = %d\n",sum);
}
else
{
printf("ENTER INTEGER NOT CHARACTER\n");
break;
}
return 0;
}
Initially I had tried this using isalpha(), and the program worked fine when adding numbers but interpreted characters as zeros instead of printing the "not an integer" statement. However, now that I reworked it to use isdigit(), it does not recognize ANY input as an integer, whether or not it is. Is there something that I'm just doing wrong?
When you use scanf to read an integer, you get just that, an integer. (To read a single character, you need %c and a pointer-to-char).
When you use isdigit(), you need to supply the representation of that character (e.g. in ASCII, the character '0' has the representation 48, which is indeed its value as an integer). To recap:
isdigit(0) is false
isdigit(48) is true (for ASCII, ISO8859, UTF-8)
isdigit('0') is true (no matter the character set)
isdigit('0' + n) is true for integers n = 0 ... 9
PS: Not testing the return value from scanf is asking for trouble...
Neither isdigit nor isalpha work as you think they do. The intent of those library functions is to check whether a given code point, represented as an int, is within a subset of points defined by the standard to be digit characters or alpha characters.
You should be checking the results of your scanf calls rather than assuming they just work, and acting on those results accordingly. If you request an integer and one is successfully scanned, then it will tell you so. If that fails, your course of action is probably to consume the rest of the line (through newline or EOF) and possibly try again:
#include <stdio.h>
int main()
{
int n,value,sum=0;
printf("Enter the Number of Integers You Want to Add\n");
if (scanf("%d", &n) == 1 && n > 0)
{
printf("Enter %d Integers\n", n);
while (n--)
{
if (scanf("%d", &value) == 1)
{
sum = sum + value;
}
else
{
// consume the rest of the line. if not EOF, we
// loop around and try again, otherwise break.
while ((value = fgetc(stdin)) != EOF && value != '\n');
if (value == EOF)
break;
++n;
}
}
printf("Sum of Entered Integers = %d\n", sum);
}
return 0;
}
Properly done you should be able to enter valid integers beyond single digits (i.e. values > 10 or < 0), which the above allows.
The %d marker to scanf tells it to interpret the input as a number (more accurately, it indicates that the pointer in the arguments points to an integer type). It can't do anything but put an integer into that argument. If it can't interpret the input as a number, scanf stops scanning the input and returns immediately.
isdigit() evaluates its argument as a character code, as Jens points out above. However, scanf already turned the character code into a pure number.
From the scanf man page:
On success, the function returns the number of items of the argument list
successfully filled.
In your program, you are trying to read just one item from stdin, so scanf should return 1. So check for that and you'll know that it all worked out ok:
printf("Enter the Number of Integers You Want to Add\n");
while(scanf("%d", &n) != 1) {
printf("That's not a valid integer. Please try again.\n");
}
You cannot use isdigit() the way you are using it because you're already using scanf to convert the user input to an integer. If the user had not input an integer, scanf would have already failed.
Look at the man pages for all the C functions you are using, they will show you what the function expects and what the return values will be under different circumstances.
In the case of isdigit(), the input is expected to be an unsigned char representing an ASCII character. This can be a bit confusing because ASCII characters are in fact represented as a type of integer, and a string is an array of those. Unlike languages like Python which hide all that from you. But there is a big difference between the STRING of a number (array of characters that contain the characters of the digits of the number) and the INTEGER itself which is in a form the processor actually uses to do math with... (simplified explanation, but you get the idea).

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.

Why does scanf get stuck in an infinite loop on invalid input? [duplicate]

This question already has answers here:
Why is scanf() causing infinite loop in this code?
(16 answers)
Closed 9 years ago.
In line 5 I read an integer and isint is getting 1 if it reads an integer or 0 if it's not an integer. If isint is 0 I have a loop asking user to give an integer and I read until the user gives an integer. I try this code giving a character instead of an integer but I have an infinite loop. The program just doesn't wait to give a new input. What's wrong with my code?
#include <stdio.h>
int main(void) {
int arg1;
//int arg2;
int attacknum = 1;
int isint = 1;
//printf("Insert argument attacks and press 0 when you have done this.\n");
printf("Attack %d\n", attacknum);
attacknum++;
printf("Give attacking argument:");
isint = scanf("%d", &arg1); //line 5
while(isint == 0){
printf("You did not enter a number. Please enter an argument's number\n");
isint = scanf("%d", &arg1);
printf("is int is %d\n", isint);
}
return 0;
}
As others have mentioned, if scanf can't parse the input, it leaves it unscanned.
Generally scanf is a poor choice for interactive input because of this kind of behavior, and because it doesn't match the line-at-a-time interface experienced by the user.
You are better off reading one line into a buffer using fgets. Then parse that line using sscanf. If you don't like the input, throw the whole line away and read another one.
Something like this:
#include <stdio.h>
int main(void)
{
char line[256];
int arg1;
int isint;
while (1) {
printf("Give attacking argument:");
fgets(line, sizeof line, stdin);
isint = sscanf(line, "%d",&arg1);
if (isint) break;
printf("You did not enter a number.Please enter an argument's number\n");
}
printf("Thanks for entering %d\n", arg1);
return 0;
}
(For production code you'll want to handle long lines, check return codes, also check for trailing garbage after the number, etc.)
Actually, an even better approach would be to not use scanf if you just want to read an integer, and instead use strtol. That gives you a handy pointer to the character just after the number, and you can check that it's whitespace or nul.
When scanf is confronted with a non-digit it will not consume any input and return that zero integers were read. The non-digit will stay in the input for the next call to scanf that will behave the same as the first call, etc.
In answer to your question below. You could use fgetc to parse at least one character, but this will give the error messages for every character already typed. Typically I think you want to skip until a newline. To this end you could use fgets as suggested by poolie. Or you could add the following after scanf.
int ch;
if (isint == 0)
while ((ch = fgetc(stdin)) != EOF && ch != '\n')
{
/* Skip characters */
}
P.S: In your case it is probably better to put it just before the first printf in the loop.

Resources