How to check whether an input string consist of integers - c

I'm trying to make a while loop where the condition checks that the first four elements of a string are not integers. Here's my code, somehow it doesn't work. I tried using the isdigit function from the ctype.h header.
char tr_code[200];
char *endptr;
scanf("%s", &tr_code);
fd_code=strtol(tr_code,&endptr,10);
while(strlen(tr_code)!=4 && isdigit(tr_code[0])==0 && isdigit(tr_code[1])==0 && isdigit(tr_code[2])==0 && isdigit(tr_code[3])==0)
{
printf("\nInvalid Code. please enter another '4-digit' Code: ");
scanf("%s", &tr_code);
fd_code=strtol(tr_code,&endptr,10);
}

You're using &&, but || is what you should be using:
while(strlen(tr_code) != 4 || !isdigit(tr_code[0]) || !isdigit(tr_code[1]) || !isdigit(tr_code[2]) || !isdigit(tr_code[3]))
With &&, any input that's four characters long, or has a digit in any of the first four positions (even if that memory is leftovers from the last input, since the string could be shorter) will pass.

IIRC 'scanf' signature returns an integer, which is number of characters read at the terminal.

Related

How to check if user input numbers instead of string(array of char) in C

So, I got this assignment as a student that ask me to create a simple program using C.
This program input only allow you to input only characters A-Z, a-z, and (space).
and the length of the string should be no less than 1 character and no more than 100 characters.
So, I come with the conclusion that I should use if function to validate if the user input the allowed character.
#include <stdio.h>
#include <ctype.h>
int main()
{
char name[100];
scanf("%s",&name);
fflush(stdin);
if (isdigit(name))
^^^^
{
printf("Wrong answers");
getchar();
}
else
....
It was supposed to print "wrong answers" if you input numbers in there, but this program won't run.. It keeps saying :
error C2664: 'isdigit' : cannot convert parameter 1 from 'char [100]' to 'int'
I don't know what this error means.. Is there something I miss? Or am I using the wrong function?
I have also tried
if (((name>='A')&&(name<='Z'))||((name>='a')&&(name<='z')||)((name==' ')))
{
//this print what i want
}
else
{
printf("wrong answers");//this print "wrong answer"
}
but it always print "wrong answers" no matter I input the correct input or the wrong input.
Your help is highly appreciated.
Thank you.
*ps : I am a beginner at programming.
isdigit() takes an int as argument, not a char*:
int isdigit(int c);
You have to use a loop over the string and check each character in it.
Having said that, to achieve:
this program input only allow you to input only characters 'A'-'Z', 'a'-'z', and ' '(space)
you are better off using isalpha().
Try this out:
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char name[101], temp;
// take input one character at a time
while(scanf("%c", &temp)){
// stop when newline or carriage return
if(temp == '\n' || temp == '\0' || !isalpha(temp) ){
break;
}
// save character in array
name[i] = temp;
// move to the next position of the array
i++;
}
printf( "%s", temp );
return 0;
}
The problem you're seeing is that you're passing isdigit the wrong type of value - it expects an int, but you're passing it an array of char. You would have to loop over each and every character in your string to check if it's a digit or not.
But that is ultimately not what you're after as you're looking to confirm that the string contains letters or spaces - there are lots of characters that could be entered that aren't classed as digits that would be accepted incorrectly.
What would be the easiest solution for you, is to use the function strspn. It takes a string and returns the length of how many characters match the second parameter. If that length is the same length as your string, you know that it only contains valid characters.
size_t valid;
valid=strspn(name, "abcdefg(fill in with other valid characters)");
if(valid==strlen(name))
{
// Valid name
}
else
{
// Not valid
}
If you need to expand the accepted characters, it's just a simple case of adding them to the 2nd parameter.
OP's code fails as isdigit() test is a single character is a digit (0-9). It does not test a string.
int isdigit(int c);
The isdigit function tests for any decimal-digit character.
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.
OP's buffer is too small to save 100 characters read from the user. At least 1 more needed to detect if too many were read and 1 more for a null character to mark the end of a string.
fflush(stdin); has its problems too.
scanf("%s",&name); does not save white-space. The parameter should have been name too. (no &)
Read a line of user input with fgets() which saves the result as a string.
Test if the input meets the criteria.
Read
#define N 100
// No need to be stingy on buffer size reading user input. Suggest 2x
// We need +1 for extra character detection, \n and \0
char buf[2*N + 1 + 1];
fgets(buf, sizeof buf, stdin);
// lop off potential \n
size_t length = strlen(buf);
if (length > 0 && buf[length-1] == '\n') {
buf[--length] = '\0';
}
Test
only characters 'A'-'Z', 'a'-'z', and ' '(space).
for (size_t i = 0; i<length; i++) {
if (!isalpha((unsigned char)buf[i]) && buf[i] != ' ') {
puts("Invalid chracter");
break;
}
}
length of the string should be no less than 1 character and no more than 100 characters.
if (length < 1 || length > 100) {
puts("Input wrong length");
}
Others approaches can be used to disqualify very long inputs. IMO, very long inputs represent an attack and should be handled differently than a simple line that was a bit too long.
if (length < 1 || length > 100) {
if (length + 2 >= sizeof buf) {
puts("Input way too long");
exit (EXIT_FAILURE);
}
puts("Input wrong length");
}
name must have one extra space for the \0 (NUL) character.
So to store 100 characters, its size should be at least 101.
char name[101];
You could first use
fgets(name, sizeof(name), stdin);
to read into name character array.
Note that fgets() will read in the trailing newline (\n) as well which need be removed like
name[strlen(name)-1]='\0';
Then use sscanf(). Like
size_t l=strlen(name);
sscanf(name, "%100[A-Za-z ]", name);
if(strlen(name)!=l)
{
printf("\nInvalid input.");
}
Note the space after the A-Za-z.
The 100 in the %100[A-Za-z] denotes reading at most 100 characters into name. The [A-Za-z ] will make the sscanf() stop reading if a non-alphabetic character which is not a space is encountered.
First read into name. Then store its length in l. Now read everything till a non-alphabet other than a space occurs in name to name itself (thereby modifying name).
Now compare the length of this new string with l. If they are the same. The input is valid as per your need.
You could also use scanf() instead of fgets() like
scanf("%100[^\n]", name);
which instructs to read every character till a \n into name. If this is used, no \n will added at the end of name unlike the case with fgets().
Now I would like to point out some mistakes in your code.
scanf("%s",&name);
will lead to errors. Correct one is
scanf("%s",name);
as the second argument here must be an address and since an array name in C decays into its base address, just name would do instead of &name.
As others have pointed out, using fflush() on stdin is undefined and must be avoided.
If you are trying to clear the input buffer till the next newline (\n), you could do
int ch;
while((ch=getchar())!='\n');// && ch!=EOF)
The argument of isdigit() must be a char and not a character array (type char [100] if size is 100).
And if is a statement and not a function.

Scanning the characters properly except '+' and '-'

I am trying to scan an arithmetic expression like : 4+3-2*6*(3+4/2)#
What I tried is following code. It's running fine and scanning each character properly except '+' and '-'.
Why it is not scanning only two particular characters!
void scan(){
int n,tmp,digit_no;
char c;
scanf("%c",&c);
while(c!='#'){
if(isdigit(c))
{
tmp=c;
scanf(" %d",&n);
digit_no=numPlaces(n);
n=(tmp-48)*ipow(10,digit_no)+n;
push_n(n);
n=0;
}
else if(c=='+' || c=='-' || c=='*' || c=='/' || c=='(' || c==')' || c=='=' || c=='^')
push_o(c);
scanf("%c",&c);
}
}
Do not get a char, test for a digit, scan an int and then try to put them together. This fails the code's intent with input like for input like "3-2", "1 23", "1+23" as well explained by #John Bollinger as scanf("%d",&n) is consuming the + -.
Instead put the digit back into stdin and then scan for the int.
if(isdigit(c)) {
ungetc(c, stdin);
scanf("%d",&n); // cannot fail as first character is a digit - may overflow though
push_n(n);
n=0;
}
Also suggest to detect EOF and use is...() functions correctly.
// char c;
// scanf("%c",&c);
int c;
// while(c!='#'){
while((c = fgetc(stdin)) !='#' && c != EOF) {
...
// scanf("%c",&c);
}
Detail: is...() expects an int in the range of unsigned char and EOF. Calling them with a char is a problem when the value is negative.
You are mistaken: scanf() is scanning the '+' and '-'. It is simply scanning them as part of the decimal number that follows.
With your example input, the program first scans a '4', which is a digit. It then proceeds to execute scanf(" %d",&n);, which scans the next two characters, "+3", as the number 3, because fields described by %d may optionally have a leading sign character, either '-' or '+'. The scanning stops at the first '-', since a decimal number cannot contain an internal or trailing '-', and indeed, the '-' is scanned as the next character. You would see different results for '-' or '+' following a two-digit number or a parenthesis.
Overall, your approach to scanning numbers is fundamentally flawed. Not only does it run aground on the optional sign character, but I see no way it can do the right thing when the second-most-significant digit of the number you are trying to read is a '0'. That is, your approach cannot distinguish "401" from "41". You cannot successfully scan the tail of a number as a number in its own right without losing information you need.

Generating a dice game - C Programming

I'm following a tutorial on youtube and was doing a dice generator.
It basically print out 3 dice result and sum out the dice result.
After which, the user will look at the sum, and based on the sum, the user going to guess whether the next roll is going to be higher,lower, or the same.
Below is my code, suppose, when I typed 'yes', it should be doing the code inside the if statement. However, it went straight to the else statement. Can someone please tell me what's wrong?
int answer;
int guess;
int diceRoll4 = 0;
printf("Would you like to guess your next dice? Y/N \n");
scanf(" %c", &answer);
if (answer == 'yes' ){
printf("What is your guess?\n");
printf("please key in your number \n");
scanf(" %d", &guess);
if (guess > diceRoll4 ){
printf(" You got it wrong, too high!");
}
else if (guess < diceRoll4){
printf(" You got it wrong, too low!");
}
else {
printf("You got it right");
}
}
else{
printf("Thanks for playing");
}
First of all, answer should be an array of chars in order to hold a string. Change
int answer;
to
char answer[10]; //Or any other reasonable size
Secondly, since you want to scan a string and not a character, change
scanf(" %c", &answer);
to
scanf("%9s", answer);
The 9 will scan a maximum of 9 characters (+1 for the NUL-terminator at the end), thus preventing buffer overflows.
I've removed & as %s expects a char* while &answer will give a char(*)[10]. Name of an array gets converted into a pointer to its first element char*, exactly what %s expects. The above scanf is thus equivalent to
scanf("%9s", &answer[0]);
Thirdly, comparing two strings using == compares pointers and not the actual content in them. Use strcmp from string.h instead. It returns 0 when both its arguments hold the same content. Change
if (answer == 'yes' ){
to
if (strcmp(answer, "yes") == 0){
Double quotes are used to denote a NUL-terminated string(char*), which is exactly what strcmp expects, while single quotes, as in your code, is a multi-character literal whose value is implementation-defined.
'yes' is a multi-byte character whose behaviour is implementation-defined.
What you probably want is to read and compare a single char:
if (answer == 'y' ){
or read a whole string and compare:
char answer[128];
scanf("%s", answer);
if ( strcmp(answer,"yes") == 0 ){
...
}
Notice that I changed the type of answer and used %s to read a string.
If you do not want to read in a string, but only a single char where the user can answer either Y or N, you should change int answer; to char answer;. You can then go on using your original scanf()-call. You will still need to change
if (answer == 'yes')
to
if (answer == 'Y')
If you want the user to either type in y or Y you could user toupper() from ctype.h and change your if-condition to if (toupper(answer) == 'Y').
To test the equality you have to use strcmp. If the returning value is 0 it means that they are equal.
if (strcmp(answer, "yes") == 0) {
// ...
} else {
// ...
}
Notes:
Using just answer == 'yes' it test the equality of pointers not value. This is the reason why enters only in else.
Because answer is int you have to change to an array
char answer[15]
As #Sathya mentioned you are reading just a char %c for reading a string you have to use %s
scanf("%s", answer);
Instead of 'yes' which is multi-character character constant change to "yes" that is an array of char with \0 at the end, more informations here.
this line:
if (answer == 'yes' ){
has several problems.
1) the definition of 'answer' is 'int' but the scanf is inputting a single character
2) answer could be compared with 'y' or 'n' but not to a array of char.
3) since the scanf only input a single char
and you/the user input 'yes',
only the first character was consumed,
so the 'es' are still in the input buffer
4) note the the single character could be anything, except white space.
the leading space in the format string would consume any white space.
so the user could input say 'y' or 'Y'
these are different characters
however, using the toupper() macro from ctypes.h
would mean only a 'Y' would need to be compared
5) if you decide to read a string,
then 'answer' needs to be a character array,
say: char answer[10];
and the scanf needs to have a max length modifier
on the associated "%s" input/conversion parameter
so as to avoid the user overflowing the input buffer
and the comparison would be via the strcmp() function
6) always check the returned value (not the parameter value)
from scanf to assure the operation was successful
7) diceRoll4 and guess can never be a negative number
so the variable definitions should be unsigned
and the associated scanf() for guess should use
something like "%u"
8) on the printf() format strings, always end them with '\n'
so the sting will be immediately displayed to the user,
otherwise, they will only be displayed
when a input statement is executed or the program exits

C: Using scanf to accept a predefined input length char[]

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.

How to terminate a loop when a letter entered in C?

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

Resources