Unexpected Results Using isdigit(x) - c

I am using the following code. I expect the output to be "Yes", but I instead get "No." I must be missing something very simple and fundamental.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int n = 3;
if (isdigit(n))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}

isdigit checks whether the character passed to it is a numeric character. Therefore, its argument should be char type or int type which is the code of a character.
Here, you are passing 3 to isdigit. In ASCII, 3 is the code of the character ETX (end of text) which is a non-numeric character. Therefore, isdigit(3) returns false.

isdigit() expects a character code, while you expect it to accept a plain number.
Those expectations do not mesh well.
Character literals:
'0' ordinal 0x30 -- 48
'1' ordinal 0x31 -- 49
'2' ordinal 0x32 -- 50
... You get the drift

'3' is not a digit, here, in the way isdigit() considers them. Change int n = 3 to int n = '3'. If your system uses ASCII, for instance, 3 is the end of text marker, whereas 51, equivalent to '3', is the actual character three.

isdigit expects a character. If the character passed is digit then it returns non zero. 3 is an ASCII value of non-printable character, i.e it doesn't represent a digit and that's why your else body gets executed.

man isdigit:
These functions check whether c, which must have the value of an
unsigned char or EOF, falls into a certain character class according to
the current locale.
3 is an integer, not a character. In fact, 3 is also the value of a character that is not a digit.

The string "\03\t\nABCabc123" is composed of 12 characters. The first 9 characters all return false when applied to the isdigit() function. The last 3 all return true;
#include <ctype.h>
#include <stdio.h>
int main(void) {
char data[] = "\03\t\nABCabc123";
// data[0] = 3;
char *ptr = data;
while (*ptr) {
printf("%4d --> %s\n", *ptr, isdigit((unsigned char)*ptr) ? "true" : "false");
ptr++;
}
return 0;
}

Here is the implementation of function isdigit:
int isdigit(char c)
{
return '0' <= c && c <= '9';
}
In simple words, function isdigit takes a char argument as input:
If the input represents a decimal digit in ASCII format, then the function returns 1.
If the input does not represent a decimal digit in ASCII format, then the function returns 0.
When you call function isdigit with an int argument, the argument is first truncated to a char. That doesn't make any difference in your example, since 3 fits into a char, so no information is lost due to the truncation. However, since 3 does not represent any decimal digit in ASCII format, the return-value of isdigit(3) is 0.
To summarize the above:
The return-value of isdigit('0') is 1.
The return-value of isdigit('1') is 1.
...
The return-value of isdigit('9') is 1.
In all other cases, the-return value of isdigit(...) is 0.
Your mistake probably stems from the assumption that '3' == 3. If you want to check whether or not an int variable stores a single-decimal-digit value, then you can implement a slightly different function:
int isintdigit(int i)
{
return 0 <= i && i <= 9;
}

Related

How to use isdigit function in C

I'm a complete beginner doing the cs50 course and I need to check if an argument from a user is a digit or not.
this is the code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
int main(void)
{
int i = 1;
if (isdigit(i) == 1)
{
printf("i is a digit");
}
else if (isdigit(i) == 0)
{
printf("i is not a digit");
}
return 0;
}
When I run this code I basically get a reverse of what I should be getting. When i is a number it prints out "i is not a number" and visa versa.
What am I doing wrong? I thought isdigit returns a non-zero value if it is a digit and 0 if not. Basically 1 being true and 0 being false. Is this not correct?
Much appreciated, Thanks!
What am I doing wrong?
"The isdigit function tests for any decimal-digit character". i with a value of 1 is not a digit character.
Try i = '1';. Then i will have the value of a digit character.
Code is testing the return value incorrectly. #tadman. is...() returns 0 or non-zero.
// if (isdigit(i) == 1)
if (isdigit(i))
Note: is...(int ch) functions are only valid for ch in the unsigned char range and EOF.
If you read the documentation for isdigit() you'll note the return value is expressed as:
Non-zero value if the character is a numeric character, zero otherwise.
In other words, don't compare to exactly one, that's not assured. Compare to non-zero.
That being said, this works on characters not integers, although in C the line is blurred. What you want is to ensure this is part of a string, like:
char* n = "12345";
if (isdigit(n[0]) == 0) {
...
}
In your case you're asking if ASCII character 1 is a digit, which it is not. That's the "Start of Heading" (SOH) control character.
isdigit is a character classification function.
It will return zero or non-zero depending on if a character (or rather, a character promoted to an int) is a digit.
For example:
int i = '1'; // Initialize to the character '1'
if (isdigit(i))
{
printf("i is a digit");
}
else
{
printf("i is not a digit");
}
Note that isdigit returns a non-zero value for digit characters, it doesn't have to be 1.
The argument to isdigit should be a single character, not a number. Thus your test code should more properly be
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = '1';
if(isdigit(c))
printf("c is a digit");
else
printf("c is not a digit");
return 0;
}
Your code should stick to the definition of the function, and not make assumptions about the returned value if it is a digit - the actual returned value may be implementation dependent, here's a better C-style, though as others have have said, you should be passing in a char, not an int - did the compiler not complain about that?
if (isdigit(i)) {
printf("i is a digit");
} else {
printf("i is not a digit");
}

atoi ignores a letter in the string to convert

I'm using atoi to convert a string integer value into integer.
But first I wanted to test different cases of the function so I have used the following code
#include <stdio.h>
int main(void)
{
char *a ="01e";
char *b = "0e1";
char *c= "e01";
int e=0,f=0,g=0;
e=atoi(a);
f=atoi(b);
g=atoi(c);
printf("e= %d f= %d g=%d ",e,f,g);
return 0;
}
this code returns e= 1 f= 0 g=0
I don't get why it returns 1 for "01e"
that's because atoi is an unsafe and obsolete function to parse integers.
It parses & stops when a non-digit is encountered, even if the text is globally not a number.
If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0
Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...
It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.
Example of usage:
const char *string_as_number = "01e";
char *temp;
long value = strtol(string_as_number,&temp,10); // using base 10
if (temp != string_as_number && *temp == '\0')
{
// okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
}
else
{
printf("Cannot parse string: junk chars found at %s\n",temp);
}
You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.
int Input2Integer( Str )
Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.
First: Set return value to zero.
Second: Loop over string while it is not null '\0'.
Third: return when the input character is not a valid digit.
Fourth: modify the return value based on the valid input character.
Then come back and explain why atoi works the way it does. You will learn. We will smile.

Why strlen() returning 2 or 1 everytime?

Why doesn't strlen() return an exact value?
For example, when I input 123 it returns 2. For 12345 it returns 1. Why?
#include<stdio.h>
#include<string.h>
int main()
{
long int ui,remainder,i=0,len;
char binary[20];
scanf("%ld",&ui);
while(ui!=0) {
remainder=ui%2;
binary[i]=(char)remainder;
printf("%d ",remainder);
ui=ui/2;
i++;
}
binary[i]='\0';
printf("len is %ld\n",strlen(binary));
for(i=len-1;i>=0;i--) printf("%d",binary[i]);
return 0;
}
strlen returns the length of an array of characters until it finds the character '\0' (with numeric value 0) in it. Your binary array is an array of characters, but you are treating them as integers as you're storing the numeric values 0 and 1. When calculating its length, strlen stops when it finds the first 0 you wrote.
To get the answer you need, change the binary definition to char binary[20] = { 0 };, save the remainder values as ASCII characters (binary[i] = '0' + (char)remainder;), and print them as characters printf("%c ",binary[i]);
The reason it isn't working is because of how you're using binary.
The strlen function is meant to operate on NULL terminated strings. But binary isn't treaded as a string, but as an array of bytes.
The strlen function searches until it find a null bytes, i.e. a byte with a value of 0. In the case of "123", the value 0 appears after two bytes. In the case of "12345", the value 0 appears after one byte. This explains the output you're getting.
Also, you're using len without initializing it. This leads to undefined behavior. You want to set it to i after you exit the while loop. You also need to change the format specifier for your first printf from %d to %ld since remainder is declared as a long int.
There are a few problems here binary is a char so it's '0' + value print it out that way as well;
#include<stdio.h>
#include<string.h>
int main()
{
long int ui,remainder,i=0,len;
char binary[20] = {0};
scanf("%ld",&ui);
if (ui==0) { binary[i] = '0'; i++; } // Special case for zero
while(ui!=0) {
remainder=ui%2;
binary[i]=(char)remainder + '0';
printf("%d ",remainder);
ui=ui/2;
i++;
}
binary[i]='\0';
printf("len is %ld %s\n",len = strlen(binary), binary);
for(i=len-1;i>=0;i--)
printf("%c",binary[i]);
return 0;
}
For my run below I got
32
0 0 0 0 0 1 len is 6 000001
100000

Convert a char to an int in C

I want to make a program which converts 3www2as3com0 to www.as.com but I have got trouble at the beginning; I want to convert the first number of the string (the character 3) to an integer to use functions like strncpy or strchr so when I print the int converted the program shows 51 instead of 3. What is the problem?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
/* argv[1]--->3www2as3com0*/
char *string;
char *p;
string=argv[1];
p=string;
char cond,cond2;
cond=*p; //I want to have in cond the number 3
cond2=(int)cond; //I want to convert cond (a char) to cond2(an int)
printf("%d",cond2); //It print me 51 instead of 3
return (EXIT_SUCCESS);
}
Your computer evidently encodes strings in a scheme called ASCII . (I am fairly sure most modern computers use ASCII or a superset such as UTF-8 for char* strings).
Notice how both printable and nonprintable characters are encoded as numbers. 51 is the number for the character '3'.
One of the nice features of ASCII is that all the digits have increasing codes starting from '0'.
This allows one to get the numerical value of a digit by calculating aDigitCharacter - '0'.
For example: cond2 = cond - '0';
EDIT:
You should also probably also double check that the character is indeed a digit by making sure it lies between '0' and '9';
If you want to convert a string containing more than one digit to a number you might want to use atoi.
It can be found in <stdlib.h>.
The character's integer value is the ASCII code for the digit, not the number it actually represents. You can convert by subtracting '0'.
if( c >= '0' && c <= '9' ) val = c - '0';
Seems like the strings you are using will never have negative number, so you can use atoi(), returns the integer value from char. If it encounters something that is not a number, it will get the number that builds up until then.

Why do I get this unexpected result using atoi() in C?

I don't understand the results of the following C code.
main()
{
char s[] = "AAA";
advanceString(s);
}
void advanceString(p[3])
{
int val = atoi(p);
printf("The atoi val is %d\n",val);
}
Here the atoi value is shown as 0, but I could not figure out the exact reason.
As per my understanding, it should be the summation of decimal equivalent of each values in the array? Please correct me if I am wrong.
atoi() converts a string representation of an integer into its value. It will not convert arbitrary characters into their decimal value. For instance:
int main(void)
{
const char *string="12345";
printf("The value of %s is %d\n", string, atoi(string));
return 0;
}
There's nothing in the standard C library that will convert "A" to 65 or "Z" to 90, you'd need to write that yourself, specifically for whatever charset you're expecting as input.
Now that you know what atoi() does, please don't use it to deal with numeric input in whatever you come up with. You really should deal with input not being what you expect. Hmm, what happens when I enter 65 instead of A? Teachers love to break things.
atoi() doesn't do any error checking whatsoever, which makes anything relying on it to convert arbitrary input fragile, at best. Instead, use strtol() (POSIX centric example):
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
static const char *input ="123abc";
char *garbage = NULL;
long value = 0;
errno = 0;
value = strtol(input, &garbage, 0);
switch (errno) {
case ERANGE:
printf("The data could not be represented.\n");
return 1;
// host-specific (GNU/Linux in my case)
case EINVAL:
printf("Unsupported base / radix.\n");
return 1;
}
printf("The value is %ld, leftover garbage in the string is %s\n",
// Again, host-specific, avoid trying to print NULL.
value, garbage == NULL ? "N/A" : garbage);
return 0;
}
When run, this gives:
The value is 123, leftover garbage in
the string is abc
If you don't care about saving / examining the garbage, you can set the second argument to NULL. There is no need to free(garbage). Also note, if you pass 0 as the third argument, it's assumed the input is the desired value of a decimal, hex or octal representation. If you need a radix of 10, use 10 - it will fail if the input is not as you expect.
You'd also check the return value for the maximum and minimum value a long int can handle. However, if either are returned to indicate an error, errno is set. An exercise for the reader is to change *input from 123abc to abc123.
It's important to check the return, as your example shows what happens if you don't. AbcDeFg is not a string representation of an integer, and you need to deal with that in your function.
For your implementation, the most basic advice I can give you would be a series of switches, something like:
// signed, since a return value of 0 is acceptable (NULL), -1
// means failure
int ascii_to_ascii_val(const char *in)
{
switch(in) {
// 64 other cases before 'A'
case 'A':
return 65;
// keep going from here
default:
return -1; // failure
}
.. then just run that in a loop.
Or, pre-populate a dictionary that a lookup function could scope (better). You wouldn't need hashes, just a key -> value store since you know what it's going to contain in advance, where the standard ASCII characters are keys, and their corresponding identifiers are values.
It tries to convert the string into an integer. Since AAA cannot be converted into an integer the value is 0. Try giving it 42 or something.
If no valid conversion could be
performed, a zero value is returned.
See http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/
Read atoi() as a to i (ASCII to integer).
atoi() converts a string representing a decimal number, to integer.
char s[] = "42";
int num = atoi(s); //The value of num is 42.
atoi expects its argument to be a string representation of a decimal (base-10) integer constant; AAA is not a valid decimal integer constant, so atoi returns 0 because it has no other way to indicate that the input is invalid.
Note that atoi will convert up to the first character that isn't part of a valid integer constant; in other words, "123" and "123w" will both be converted to 123.
Like everyone else is saying, don't use atoi; use strtol instead.

Resources