Output the decimal versions of unknown input - c

I'm trying to write a short script to take in 6 inputs that will be in decimal, hex or octal and output their decimal versions. For example, if I input 1, 1 should be output. Input 010, should get 8, input 0x20, should get 32. Do I have to test each value to see if scanf reads it as its type or can I cast them after scanf reads them all as is? Also do I need functions to convert the octal and hex values to decimal or can I cast them? I'm very new to C and don't understand it well yet, but here's what I have so far (it outputs -200 as 32765 for whatever reason):
int num[6];
printf("Enter six integers:\n");
int i = 0;
int j;
while (i < 6){
if(scanf("0x%x", &j) == 1){
scanf("0x%x", &num[i]);
//hexToDec(num[i]);
} else if (scanf("0%o", &j) == 1){
scanf("0%o", &num[i]);
//octalToDec(num[i]);
} else {
scanf("%i", &num[i]);
}
i+=1;
}
for(int p = 0; p < 6; p+=1){
printf("%i\n", num[p]);
}

Answer for future reference: simply scanning in with "%i" does the conversions automatically. Revised code below:
int main(){
int num[6];
printf("Enter six integers:\n");
int i = 0;
while (i < 6){
scanf("%i", &num[i]);
i+=1;
}
for(int p = 0; p < 6; p+=1){
printf("%i\n", num[p]);
}
}

You cannot use scanf("0x%x", &j) to test the input and then use scanf("0x%x", &num[i]) to put the value in num[i]. scanf consumes the characters it accepts, so they are no longer in the input stream. When you do the second scanf, the characters are gone.
Instead, just attempt to scan the desired thing. If it works, you are done. If it does not work, go on to an else:
if (scanf("0x%x", &num[i]) == 1)
; // Worked, nothing to do.
else if (scanf("0%o", &num[i]) == 1)
;
else if (scanf("%i", &num[i]) == 1)
;
else
{
// Nothing worked, should put error-handling code here.
}
That is actually not great code for real applications, because scanf will consumes some of the input even if it ultimately fails. For example, with scanf("0x%x", &num[i]), if the input contains “0x” but then contains a non-hexadecimal character, scanf will consume the “0x” but leave the next character. However, it suffices for a learning exercise.
Once you have values in the num array, they are just int values. They are mathematical values, not numerals that are in octal, decimal, or hexadecimal. You can print them with %o for octal, or %d or %i for decimal. The original numeral is irrelevant. When printing, the mathematical value will be used to format a string in the requested base.
You should not use %x for printing an int, as %x is for unsigned int. However, you could convert an int to unsigned int and then print it with %x.
Note that you should not scan to int objects with %x. %x is for scanning to unsigned int objects. You can actually scan hexadecimal, octal, and decimal using:
if (scanf("%i", &num[i]) == 1)
;
else
…
The %i specification will recognize hexadecimal numerals beginning with “0x”, octal numerals beginning with “0”, and decimal numerals.
If you did not want to use %i for all three, because you want direct control for some reason, you will need to write more code. Standard C does not provide a direct way to scan only a hexadecimal numeral to an int. You would need to get the characters from the input and calculate the value from them or scan to an unsigned int and then convert the result to an int.

You can use function strtol with a base of 0 to do auto-detection of the integral value's base according to the input strings format (cf, for example, strtol documentation at cppreference.com):
long strtol( const char *str, char **str_end, int base )
Interprets an integer value in a byte string pointed to by str. ...
The valid integer value consists of the following parts:
(optional) plus or minus sign
(optional) prefix (0) indicating octal base (applies only when the base is 8 or ​0​)
(optional) prefix (0x or 0X) indicating hexadecimal base (applies only when the base is 16 or ​0​)
If the value of base is ​0​, the numeric base is auto-detected.
So a call like long val = strtol("0x10",NULL,0); will yield a decimal value of 16.
Note that you can also use scanf in conjunction with format specifier %i, since this is defined to behave just as a call to strtol:
​int scanf( const char *format, ... )
%i matches an integer. The format of the number is the same as expected by strtol() with the value ​0​ for the base argument (base is
determined by the first characters parsed)

The quick answer is use only
if(scanf("%i", &j) == 1){
...
}
what I have so far (it outputs -200 as 32765 for whatever reason):
Yet error handing of scanf() is troublesome. Far better to read the line of user input with fgets() and then parse the line - perhaps with strtol().

Related

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

Splitting a string into integers

For my first task I am using fgets() to read a string from stdin. So I input 1234567890 on the terminal and it stores the string into a variable called str. Now I want to split the digits up and perform addition. In this case the sum would be 55 since 1+2+3+4+5+6+7+8+9+0 = 55.
How can I do that?
My code so far
#include <stdio.h>
#include <string.h>
int main(void){
char str[100];
printf("Please enter the 10 digit number \n");
fgets(str, 10, stdin);
//Separate the digits
//calculate their sum
int sum =..............
//print out the sum
printf("the sum of the digits is: %s", sum);
return(0);
}
Approach 1:
If you know for sure that you're counting for single digit integer, you can use
array indexing to read the digit-by-digit value from the input string.
convert the char value to corresponding int as per the required encoding (mostly, ASCII).
define one int variable , say res to hold the sum. perform the addition and store the result to sum.
loop untill terminating null is reached.
print out the sum using %d format specifier.
Approach 2:
Alternatively, you can do something like (again assuming,single digit integer)
Convert the whole string to integer using strtol(). You must check for the errors in that case.
define one int variable , say res to hold the sum.
perform modulo 10 (%0) on the converted interger value to take out the last digit. add and store that inres`
divide converted interger value by 10 (p /= 10).
continue to step 2 untill the result is 0.
when the converted interger value becomes 0, print the res.
P.S - Just FYI, usual way of splitting a string based on some delimiter is to use strtok().
Since you are sure the string will contain digits, subtract each character from '0' to get their numeric value.
int sum = 0;
fgets(str, sizeof str, stdin);
char *s = &str;
while(*s != '\0') {
sum += *(s++) - '0';
}
printf("the sum of the digits is: %d", sum);
First, make sure that you read only digits by using scanf, like this:
char str[11]; // 10 digits plus \0 terminator
printf("Please enter the 10 digit number \n");
scanf("%10[0-9]", str);
Now you can go through the digits one by one by indexing into str, and subtracting the code of digit zero, like this:
int nextDigit = str[i] - '0';
This should be enough information for you to complete the solution by adding a for loop around it. Make sure that you print sum using %d, not %s like you did in your code sample.

Trouble with using getchar() instead of scanf()

I'm having trouble making this exercise for C-programming. I need to use the getchar()-method instead of the scanf(). When I use the scanf, everything works perfect when I type for instance 7. However when I use the getchar() and type 7, I will get the ASCII-code of 7, not the int 7. How do I fix this?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i;
printf("Voer een getal in:\n");
fflush(stdout);
i = getchar();
//scanf("%d", &i);
if (i > -1000 && i < +1000) {
printf("het ingevoerde getal is: %d\n", i);
} else {
printf("foutieve invoer\n");
}
return EXIT_SUCCESS;
}
This is the correct behavior of getchar. While scanf's %d format specifier converts a sequence of digits to a decimal number, with getchar you need to do it yourself.
In order to do that, you need to know three things:
When the sequence of digits ends,
How to convert an ASCII code of a digit to a number, and
How to combine multiple digits into a single number.
Here are the answers:
You can decide to end the character input when the value returned by getchar is not a digit. You can use the isdigit function for that (include <ctype.h> header to use it).
You can convert a single digit character to its corresponding numeric value by subtracting the code of zero (i.e. '0') from the value returned by getchar
You can combine multiple digits into a number by starting the partial result at zero, and then multiplying it by ten, and adding the value of the next digit to it.
int num = 0;
for (;;) {
int ch = getchar();
if (!isdigit(ch)) break;
num = 10 * num + (ch - '0');
}

Use scanf() to input to multiple variables?

I am wondering if it is possible to use scanf() to read one line (actually just 1 digit from 0 to 9) and input the data as both a char variable and an integer variable so the user only has to enter the number once. Any help would be greatly appreciated. I am writing in C, using nano as my text editor, gcc as my compiler, and Ubuntu 12.04 as my OS. I also have access to Windoze, if necessary. Thanks in advance!
You can read the integer in with scanf and then convert it to a character by adding '0'. Since digits are always represented sequentially, this will convert any single digit integer to its corresponding character value.
int i;
do {
printf("Enter an integer: ");
scanf("%d", &i);
if (i < 0 || i > 9)
printf("Please try again (number must be between 0 and 9)\n");
} while (i < 0 || i > 9);
char c = i + '0';
// now i has the number and c has the character
printf("%d %c\n", i, c); // outputs, e.g., 4 4
You don't want to take the variable in twice, you want to use a cast or type conversion.
Read the variable as a character, check if it is between '0' and '9' (character zero and character nine), and if it is, cast it to an int like so: int x = (int)(my_character-'0');.
If you want to be very safe or need more-than-one-character strings, see the atoi function.

Resources