Analyze a variable in C and then decide what to do - c

Hi I'm still a C beginner and a beginner coder,
I've been trying to develop some sort of a rudimental interface via command line and would like to implement that when the user inserts something that isn't a number the loop breaks and moves on
(my dream would be that it checks when the input is equal to the word new) Anyhow here's my code snippet:
do {
printf("\nInsert the score you obtained: ");
scanf("%d", &avg);
} while ( isdigit(avg) == 0 );
Basically until the input is a number it will keep going and when something that isn't an integer is inserted it should exit the loop.

To receive something that isn't an integer, you have to scanf something that isn't an integer.
char* input;
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", input);
...
Now that you have a variable that can hold both "quit" and some number (represented as characters), you need to make a decision based on what kind of input you received.
char input[80];
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
while ( strncmp(input, "exit", 4) == 0 );
this means also that you'll have to possibly convert the "string" of the number into a number value.
char input[80];
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
int number = convert_to_number(input);
while ( strncmp(input, "exit", 4) == 0 );
but the conversion should only happen if it looks like a number
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
if ( looks_like_number(input) ) {
int number = convert_to_number(input);
}
} while ( strncmp(input, "exit", 4) == 0 );
and finally you might need to collect multiple inputs, as the loop will accept them now
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
if ( looks_like_number(input) ) {
sum = add(sum, convert_to_number(input));
}
} while ( strncmp(input, "exit", 4) == 0 );
This is only one solution of a very large number of possible ways to do this.
This solution also could be improved (like trimming white space around the "exit" flag, so a user could type " exit" or "exit " to leave. One could also make the loop not a loop, as the question seems to imply entering one number or "exit" once, while a loop will permit many numbers until exit was typed. And one could remove the silly add function which was only used to demonstrate with a word a very clear idea, when + might do.
Even if the above loop / approach isn't a perfect fit for your need, please look at it carefully because the ideas within it can be used to make your program a success.
Also, don't use isdigit for checking for an entire number. Look into sscanf which is like printf but for reading stuff out of strings. sscanf your number off the string, and check the return value to see if you found a number within the string.

isdigit (prototyped as int isdigit (int Test_Character);) is not the right function to use with testing whether an int is a digit or a number. isdigit() is used rather to test if a single character is a decimal digit (0-9). The input to this function must be a character with integer value between 0 and 255. ( refer to ASCII table to view what these value are.)
The right thing to do here is to determine if scanf converted an input value correctly.
The function scanf() is prototyped with a return value, indicating success or failure, but your code does not currently check it.
So, in summary, your while loop can be written using the return of scanf():
int main(void)
{
int count = 0;
int avg= 0;
do
{
printf("\nInsert the score you obtained: ");
count = scanf("%d", &avg);
}while(count > 0); //stays in loop for any numeric value
// } while ( isdigit(avg) == 0 );//Note; input containing decimal points will be truncated
//to contain only integer portion
return 0;
}
As long as the input is an integer numeric value, scanf will return count as a positive value (the number of values scanned correctly.)
For example:
123, -123, 8, 0 will all allow the loop to continue.
a, dfg will all result in a failed attempt to convert the input to a numeric, and return EOF (-1)
Process as string representation of numeric: (similar behavior with alternative coding approach.)
I promised to show how to use strtol():
By changing the input format specifier from "%d" to "%s", scanf() will read the input value of 123 as a string: "123". When input is brought into your program as a string representation of a numeric, it must be converted to a number a numeric type before it can be used. strtol() can be used to do this conversion.
Here is a modification of your original program illustrating how it can be done:
int main(void)
{
int count = 0;
char avg[20] = {0};//changed to string variable
long val = 0;
bool success = FALSE;
do
{
printf("\nInsert the score you obtained: ");
count = scanf("%s", avg);//changed format specifier to read strings
if(count)
{
success = parseLong(avg, &val);//see correct usage of strtol in function
}
}while(success); //Stays in loop for any numeric input.
//Note: input containing decimal values
//will be truncated to contain integer portion only.
return 0;
}
bool parseLong(const char *str, long *val)
{
char *temp;
BOOL rc = TRUE;
errno = 0;
*val = strtol(str, &temp, 0);
if (temp == str || ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
rc = FALSE;
return rc;
}

Related

How tdo i designing the function that takes the argument of interger that the minmal value?

Ok, so i'm designing the function get_num that takes as an argument an integer that represents a minimum value. The
function is supposed to repeatedly ask the user to enter a number until the user enters an integer greater than or equal
to the given minimum. The function should return the valid entry as an integer.
and the function should assume the user will only enter integer values.
int main(){
scanned = scanf(...);
while (scanned != 1 or the number scanned in was too small) {
// call scanf again....
scanned = scanf(...);
}
return 0;
}
but the problem is, when scanf is does not successfully read input from standard input that value remains in
standard input. This means the call scanf inside the loop will keep trying to scan that same invalid input
over and over again (infinite loop!)
When working with unknown inputs it is usually preferable to read the input into a buffer and try to parse or extract data from that.
In your case it would probably make sense to look something like this:
int get_num(int min) {
char buffer[1024]; // size arbitrarly chosen
while(fgets(buffer, sizeof buffer, stdin)) {
// extract number
int value;
if (sscanf(buffer, "%d", &value) != 1 || value < min) {
// print some error message
continue;
}
return value;
}
// return some "error value"
return -1;
}

Integer and Decimal related

This is the part of my program I am stuck on. When I enter say 5.5 my program will run twice, however I want it to read as 5.5 not 5 then 5 again. I understand I need to add %f (hence added a float input), however, I'm not sure how I will implement it after I do a scanf for char. I only want the program to accept integer so it cannot accept 5.5 as a value. It is in a do-while loop. Any help would be greatly appreciated, please explain thoroughly because I am still kind of new to programming.
int amount;
float input;
do{
printf("\nPlease enter integer:");
if(scanf("%d", &amount)!= 1){
//if reads a character or symbol asks to try again
printf("\nEnter numbers only!\n");
getchar();
}
...
Using scanf with an integer format (%d) and then feeding non integer values is a recipe for disaster (or at least your program not working).
Either use scanf and only type integers or read strings and convert the strings to integers.
do {
char* line = NULL;
size_t sz = 0;
printf("\nPlease enter integer:");
getline(&line, &sz, stdin);
if (line == NULL) {
// TODO error handling
// Can also check the getline() -1 is an error.
} else {
// atoi returns 0 on error, so if 0 is a valid value you'll
// need special case handling to determine if you've got a
// "valid" 0 or not.
amount = atoi(line);
free(line);
}

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).

Validating input with isdigit for a factorial program

I wrote the following code in C to make a program which calculate the factorial of any number.
I want to add to my program some validation/error handling, such as preventing random characters, floats or negative values from being entered, so I used the isdigit function.
Unfortunately there is a hidden problem which I don't know how to solve. When I enter any input it considers it to be false (i.e. not a digit) even if it's a positive digit.
#include <stdio.h>
#include <ctype.h>
int main()
{
char choice;
unsigned long long int factorial=1;
int counter,number;
for(;;)
{
printf("Please , enter a positive integer number only : ");
scanf("%d",&number);
fflush(stdin);
if(isdigit(number))
{
for(counter=number;counter>1;counter--)
factorial*=counter;
printf("The factorial of number %d is %llu",number,factorial);
}
else
{
printf("\a\aError\n");
continue;
}
printf("\n1-Press c or C if you want to calculate the factorial of a new number\n2-Press any key if you want to exit the program\n ");
scanf("%c",&choice);
if(choice=='c'||choice=='C')
{
factorial=1;
system("cls");
continue;
}
else
return 0;
}
}
You are using isdigit wrong. Read its documentation to find out what it actually does.
You probably meant:
if ( number >= 0 && number <= 9 )
However you also need to check whether scanf succeeded or not. If they type in some words, then scanf("%d" fails and does not update number, so trying to access number in that case accesses an uninitialized variable. To deal with that you could either check the return value of scanf, or do:
int number = -1;
scanf("%d",&number);
because the value will be left unchanged if the input failed.
NB. Don't use fflush(stdin)
isdigit checks a single character if that's a decimal digit character.
But, your input could be say 25, multiple characters. So, I changed a portion:L
char input[30];
for(;;)
{
printf("Please , enter a positive integer number only : ");
scanf("%s",input);
if(isdigit(input[0]))
{
number = atoi(input);
for(counter=number;counter>1;counter--)
Keeping rest of your program snippet same.
Here, isdigit is used to check if the first character in input is a digit and therefore a valid candidate to be converted by atoi into an integer value number.

How to ensure user-input data is an integer

I am new to C, but I know C#. In C#, I could use TryParse to ensure that the user typed in the correct datatype.
Here is the basic code I have for C:
int shallowDepth;
do
{
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
scanf ("%d", &shallowDepth);
if (shallowDepth < 2 || shallowDepth > 5)
{
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}
while (shallowDepth < 2 || shallowDepth > 5);
The problem is if I type characters, such as "asdf", the program goes crazy and repeatedly says "Enter a depth for the shallow end between 2-5 feet: ". I'm not sure why this is exactly happening, but it has to be because it expects an int and I'm passing characters.
So how do I verify that the user inputted data is of int type before trying to store it in a variable? Thanks.
This is happening because with %d scanf will refuse to touch anything that does not look like a number and leaves the text in the buffer. The next time around it will again reach the same text and so on.
I recommend that you ditch scanf for now and try something like fgets and then one of the functions in the strtoXXX family such as strtoul or strtoumax. These functions have a well-defined way of reporting errors and you can easily prompt the user for more text.
For example you could do:
char str[LENGTH];
long x;
if (!fgets(str, sizeof str, stdin)) {
/* Early EOF or error. Either way, bail. */
}
x = strtol(line, NULL, 10);
At this point you could use your number, but be aware that:
You can specify a pointer for strtol to fill and it will point to the first unacceptable character
If the result cannot be represented as a long then strtol will set errno = ERANGE. If you plan to test for this you must set errno = 0 before the strtol
If you want to use scanf you can't test it before. But you don't need too!
In your code if the user doesn't enter a number (or something that starts with a number), scanf returns 0, because it returns the number of parameters it could read.
So you need to check the return value of scanf to check if anything could be read.
Second, you need to remove everything that's still in the puffer.
You can use something like this for that:
while(getchar()!='\n');
If you want to handle files as well, you should catch EOF there, too.
int shallowDepth;
int invalid;
do {
int stat;
invalid = 0;
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
stat = scanf ("%d", &shallowDepth);
if(stat != 1){
invalid = 1;
while(getchar() != '\n');//clear stdin
} else if (shallowDepth < 2 || shallowDepth > 5){
invalid = 1;
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}while (invalid);

Resources