Converting a long hexadecimal string to a decimal string - c

I would need to figure out a way to do the following in standard C. Assume we are given a string representing a hexadecimal number with say n digits. We want to convert this to a string representing the same number in decimal. The strings could have an arbitrary number of digits. What's at least easy to deduce is that the decimal string needs <= 2n letters.
Once we reach the size limit for numbers that the machine can handle, then the length of the string becomes quite irrelevant, so a hexadecimal number with 100 digits should be just as easy/hard to convert as one with 1000 digits i.e. the same algorithm should work on both as it has to work in chunks.
Has anyone here thought about this? What's really annoying is that large powers of 2 have nonzero digits down to the first order, so any element in the hexadecimal sequence can affect the last digit in decimal...
I could only find questions where someone wanted a string converted to a number or a number to a string in some base. If someone wonders why I would need this. I'm playing around with arbitrary precision numbers and the most economical format for storing numbers in is base 256, so I need to print numbers given in this form (or equivalently any hexadecimal).
EDIT: So I implemented a conversion function and I'll share it here if anyone else might ever need it. This was very quick an dirty and a better implementation would of course start by replacing the digits buffer in the struct by something that gets dynamically allocated. Since all arithmetic happens in the "add" function it would then be easy to realloc this buffer to something bigger if an addition overflows. The output buffer size is always simple to assign, since any hexadecimal number converts to a decimal number with less or equal to twice the digits.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 1000
struct number {
unsigned char digits[MAXLEN];
unsigned int num_digits;
};
int add(struct number *, struct number *, struct number *);
int mult(struct number *, struct number *, struct number *);
int power(struct number *, unsigned int, struct number *);
void print_number(struct number *, char *);
void dec(struct number *);
void hex2dec(char *hex, char *outbuf)
{
int n;
char *s;
struct number decrep;
struct number twopow;
struct number digit;
decrep.num_digits = 0;
n = strlen(hex);
s = hex;
while(--n > -1) {
/* weight of digit */
twopow.num_digits = 2;
twopow.digits[0] = 6;
twopow.digits[1] = 1;
power(&twopow, n, &twopow);
/* Extract digit */
if(*s <= '9' && *s >= '0') {
digit.digits[0] = *s - '0';
digit.num_digits = 1;
} else if(*s <= 'f' && *s >= 'a') {
digit.digits[0] = *s - 'a';
digit.digits[1] = 1;
digit.num_digits = 2;
} else if(*s <= 'F' && *s >= 'A') {
digit.digits[0] = *s - 'A';
digit.digits[1] = 1;
digit.num_digits = 2;
}
s++;
mult(&digit, &twopow, &digit);
add(&decrep, &digit, &decrep);
}
/* Convert decimal number to a string */
if(decrep.num_digits == 0) {
*outbuf = '0';
*(++outbuf) = '\0';
return;
}
for(n = decrep.num_digits-1; n >= 0; n--) {
*(outbuf++) = '0' + decrep.digits[n];
}
*outbuf = '\0';
}
int main(void)
{
char buf[1000];
hex2dec("FFEa4334234FABCD", buf);
printf("%s", buf);
return 0;
}
void copy_number(struct number *dst, struct number *src)
{
int i;
for(i = 0; i < src->num_digits; i++) dst->digits[i] = src->digits[i];
dst->num_digits = src->num_digits;
}
int power(struct number *a, unsigned int n, struct number *b)
{
struct number atmp;
/* Are we exponentiating by 0? */
if(n == 0) {
b->num_digits = 1;
b->digits[0] = 1;
return 0;
}
copy_number(&atmp, a);
while(--n > 0) {
mult(&atmp, a, &atmp);
}
copy_number(b, &atmp);
return 0;
}
int mult(struct number *a, struct number *b, struct number *c)
{
struct number btmp;
struct number ctmp;
struct number *t;
/* Are we multiplying by 0? */
if(a->num_digits == 0 || b->num_digits == 0) {
c->num_digits = 0;
return 0;
}
if(a->num_digits < b->num_digits) {
t = a;
a = b;
b = t;
}
copy_number(&btmp, b);
copy_number(&ctmp, a);
while(1) {
/* Are we multiplying by 1? */
if(btmp.num_digits == 1 && btmp.digits[0] == 1) {
break;
}
add(&ctmp, a, &ctmp);
dec(&btmp);
}
copy_number(c, &ctmp);
return 0;
}
int add(struct number *a, struct number *b, struct number *c)
{
int i, j;
int carry;
struct number *t;
if(a->num_digits < b->num_digits) {
t = a;
a = b;
b = t;
}
for(i = 0, carry = 0; i < a->num_digits; i++) {
if(i >= b->num_digits) j = a->digits[i]+carry;
else j = a->digits[i]+b->digits[i]+carry;
if(j > 9) {
j -= 10;
carry = 1;
} else {
carry = 0;
}
c->digits[i]=j;
}
/* Did we overflow? */
if(carry > 0 && i == MAXLEN) return -1;
/* Carry over from last addition? */
if(carry > 0) {
c->digits[i] = carry;
c->num_digits = a->num_digits+1;
} else {
c->num_digits = a->num_digits;
}
return 0;
}
void print_number(struct number *a, char *buf)
{
int i;
if(a->num_digits == 0) {
printf("0");
return;
}
for(i = a->num_digits-1; i >= 0; i--) {
*(buf++) = '0' + a->digits[i];
}
*buf = '\0';
}
void dec(struct number *a)
{
int i;
for(i = 0; i < a->num_digits; i++) {
if(a->digits[i] > 0) {
a->digits[i]--;
break;
}
a->digits[i] = 9;
}
/* Did number of digits get lower */
if(i == a->num_digits -1 && a->digits[i] == 0) {
for(i = a->num_digits - 1; i >= 0; i--) {
if(a->digits[i] != 0) {
a->num_digits = i + 1;
break;
}
}
}
}

Please note there is a bug in this code. As soon as the HEX string contains a 0 !
this snippet :
/* Extract digit */
if(*s <= '9' && *s >= '0') {
digit.digits[0] = *s - '0';
digit.num_digits = 1;
} else if(*s <= 'f' && *s >= 'a') {
gives digits[0] = 0; num_digites = 1;
But later in the mult() function there is the snipet :
if(a->num_digits == 0 || b->num_digits == 0) {
c->num_digits = 0;
return 0;
}
that test 0 by num_digits == 0. So I suggest to replace in the first function the snippet with :
if(*s == '0') {
digit.digits[0] = *s - '0';
digit.num_digits = 0;
}else if(*s <= '9' && *s > '0') {
digit.digits[0] = *s - '0';
digit.num_digits = 1;
} else if(*s <= 'f' && *s >= 'a') {
looks to work fine for me.
You can compare the result on this site : http://www.statman.info/conversions/hexadecimal.php

Related

K&R C Programming Language Exercise 2-3 code returns rubbish

I tried to write a solution from exercise 2-3. After compilation, it returns random numbers on output. I don't really understand where this issue is coming from.
Any help appreciated.
StackOverflow keeps asking for more details. The purpose of the program is listed in the code bellow.
More delails.
Purpose of the code:
Write the function htoi(s), which converts a string of hexa-
decimal digits (including an optional 0x or 0X) into its
equivalent integer value. The allowable digits are 0 through 9,
a through f, and A through F.
/*
* Write the function htoi(s), which converts a string of hexa-
* decimal digits (including an optional 0x or 0X) into its
* equivalent integer value. The allowable digits are 0 through 9,
* a through f, and A through F.
*/
#include <stdio.h>
#include <math.h>
int hti(char s)
{
const char hexlist[] = "aAbBcCdDeEfF";
int answ = 0;
int i;
for (i=0; s != hexlist[i] && hexlist[i] != '\0'; i++)
;
if (hexlist[i] == '\0')
answ = 0;
else
answ = 10 + (i/2);
return answ;
}
unsigned int htoi(const char s[])
{
int answ;
int power = 0;
signed int i = 0;
int viable = 0;
int hexit;
if (s[i] == '0')
{
i++;
if (s[i] == 'x' || s[i] == 'X')
i++;
}
const int stop = i;
for (i; s[i] != '\0'; i++)
;
i--;
while (viable == 0 && i >= stop)
{
if (s[i] >= '0' && s[i] <= '9')
{
answ = answ + ((s[i] - '0') * pow(16, power));
}
else
{
hexit = hti(s[i]);
if (hexit == 0)
viable = 1;
else
{
hexit = hexit * (pow(16, power));
answ += hexit;
}
}
i--;
power++;
}
if (viable == 1)
return 0;
else
return answ;
}
int main()
{
char test[] = "AC";
int i = htoi(test);
printf("%d\n", i);
return 0;
}
answ is not initialized in htoi. Initialize it to zero.

addition, subtraction, and comparison of large numbers of any base between 2 and 36

I need to put some additional code for subtraction and comparison.
and i put (bool larger) for comparison.
When i run this and input 1 '<' 2 to compare, it says "Invalid operation: < ".
and also for the 1 = 1, it says "Invalid operation: = ".
but the result need to be "Result: false" and "Result: True"
I have no idea about it. I know the code is long but not so complicated. Please get me some hint if you can.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bignum_math.h"
/*
* Returns true if the given char is a digit from 0 to 9
*/
bool is_digit(char c) {
return c >= '0' && c <= '9';
}
/*
* Returns true if lower alphabetic character
*/
bool is_lower_alphabetic(char c) {
return c >= 'a' && c <= 'z';
}
/*
* Returns true if upper alphabetic character
*/
bool is_upper_alphabetic(char c) {
return c >= 'A' && c <= 'Z';
}
/*
* Convert a string to an integer
* returns 0 if it cannot be converted.
*/
int string_to_integer(char* input) {
int result = 0;
int length = strlen(input);
int num_digits = length;
int sign = 1;
int i = 0;
int factor = 1;
if (input[0] == '-') {
num_digits--;
sign = -1;
}
for (i = 0; i < num_digits; i++, length--) {
if (!is_digit(input[length-1])) {
return 0;
}
if (i > 0) factor*=10;
result += (input[length-1] - '0') * factor;
}
return sign * result;
}
/*
* Returns true if the given base is valid.
* that is: integers between 2 and 36
*/
bool valid_base(int base) {
if(!(base >= 2 && base <= 36)) {
return false;
}
return true;
}
/*
* TODO
* Returns true if the given string (char array) is a valid input,
* that is: digits 0-9, letters A-Z, a-z
* and it should not violate the given base and should not handle negative numbers
*/
bool valid_input(char* input, int base) {
/*
* check for valid base and if negative
*/
if (!valid_base(base) || input[0]=='-') {
return false;
}
else {
int len = strlen(input);
int i;
for (i =0; i< len; i++){
/*
* check if the input string is a digit/letter
*/
if (!(is_digit(input[i]) || is_lower_alphabetic(input[i]) || is_upper_alphabetic(input[i]))){
return false;
}
/*
* if the int excesses the base?
*/
else if (is_digit(input[i])){
if (input[i]-'0'>=base){ //convert char to int and compare with the base
return false;
}
}
/*
*or if the letter excesses the base?
*/
else if (is_lower_alphabetic(input[i])){
if (input[i]-'a'+10 >=base){
return false;
}
}
else if (is_upper_alphabetic(input[i])){
if (input[i] - 'A' + 10 >=base) {
return false;
}
}
}
return true;
}
}
/*
* converts from an array of characters (string) to an array of integers
*/
int* string_to_integer_array(char* str) {
int* result;
int i, str_offset = 0;
result = malloc((strlen(str) + 1) * sizeof(int));
result[strlen(str)] = -1;
for(i = str_offset; str[i] != '\0'; i++) {
if(is_digit(str[i])) {
result[i - str_offset] = str[i] - '0';
} else if (is_lower_alphabetic(str[i])) {
result[i - str_offset] = str[i] - 'a' + 10;
} else if (is_upper_alphabetic(str[i])) {
result[i - str_offset] = str[i] - 'A' + 10;
} else {
printf("I don't know how got to this point!\n");
}
}
return result;
}
/*
* finds the length of a bignum...
* simply traverses the bignum until a negative number is found.
*/
int bignum_length(int* num) {
int len = 0;
while(num[len] >= 0) { len++; }
return len;
}
/*
* TODO
* Prints out a bignum using digits and upper-case characters
* Current behavior: prints integers
* Expected behavior: prints characters
*/
void bignum_print(int* num) {
int i;
if(num == NULL) { return; }
/* Handle negative numbers as you want
* let the last digit be -2 if negative
* */
i = bignum_length(num);
if (num[i]==-2){
printf("-");
}
/* Then, print each digit */
for(i = 0; num[i] >= 0; i++) {
if (num[i]<=9){
printf("%d", num[i]);
}
else if (num[i]>9){
char digit = num[i]+'A'-10;
printf("%c", digit);
}
}
printf("\n");
}
/*
* Helper for reversing the result that we built backward.
* see add(...) below
*/
void reverse(int* num) {
int i, len = bignum_length(num);
for(i = 0; i < len/2; i++) {
int temp = num[i];
num[i] = num[len-i-1];
num[len-i-1] = temp;
}
}
/*
* used to add two numbers with the same sign
* GIVEN FOR GUIDANCE
*/
int* add(int* input1, int* input2, int base) {
int len1 = bignum_length(input1);
int len2 = bignum_length(input2);
int resultlength = ((len1 > len2)? len1 : len2) + 2;
int* result = (int*) malloc (sizeof(int) * resultlength);
int r = 0;
int carry = 0;
int sign = input1[len1];
len1--;
len2--;
while(len1 >= 0 || len2 >= 0) {
int num1 = (len1 >= 0)? input1[len1] : 0;
int num2 = (len2 >= 0)? input2[len2] : 0;
result[r] = (num1 + num2 + carry) % base;
carry = (num1 + num2 + carry) / base;
len1--;
len2--;
r++;
}
if(carry > 0) { result[r] = carry; r++; }
result[r] = sign;
reverse(result);
return result;
}
/*
* helper function for subtract
* determine which number is larger of two positive numbers
*/
bool larger(int* input1, int* input2){
int len1 = bignum_length(input1);
int len2 = bignum_length(input2);
if (len1<=len2){
if (len1<len2){ //if input1 has less digit than input2
return false;
}
int i;
for (i =0; i < len1; i++ ){//they have the same length
if (input1[i]<input2[i]){ //if the same digit in input1 is smaller than that in input2
return false;
}
}
}
return true; //else input1 is indeed larger than/equal input2 (longer or every digit is no less than that in input2
}
/*
* helper function for subtract
* subtract from the larger
*/
int* subtractLarger(int* input1, int* input2, int base){ //input1 is larger or equal than/to input2 and both positive
int len1 = bignum_length(input1);
int len2 = bignum_length(input2);
int resultlength = ((len1 > len2) ? len1 : len2) + 2;
int *result = (int *) malloc(sizeof(int) * resultlength);
int r = 0;
int carry = 0;
int sign = -1;
len1--;
len2--;
while(len1 >= 0 ) {
int num1 = (len1 >= 0)? input1[len1]-carry : 0;
int num2 = (len2 >= 0)? input2[len2] : 0;
if (num1>=num2){
result[r] = (num1-num2);
carry = 0;
}
else {
result[r]= num1+base-num2;
carry = 1;
}
len1--;
len2--;
r++;
}
if (result[r-1]==0){
result[r-1] = sign;
}
else {
result[r] = sign;
}
reverse(result);
return result;
}
/*
* used to subtract two numbers with the same sign
*/
int* subtract (int* input1, int* input2, int base) {
if (larger(input1,input2)){
return subtractLarger(input1, input2, base);
}
else {
int* res = subtractLarger(input2, input1, base); //exchange input1 and input2, note the result is negative
int sign = -2; //negative result
res[bignum_length(res)] = sign;
return res;
}
}
/*
* TODO
* This function is where you will write the code that performs the heavy lifting,
* actually performing the calculations on input1 and input2.
* Return your result as an array of integers.
* HINT: For better code structure, use helper functions.
*/
int* perform_math(int* input1, int* input2, char op, int base) {
/*
* this code initializes result to be large enough to hold all necessary digits.
* if you don't use all of its digits, just put a -1 at the end of the number.
* you may omit this result array and create your own.
*/
int resultlength = bignum_length(input1) + bignum_length(input2) + 2;
int* result = (int*) malloc (sizeof(int) * resultlength);
if(op == '+') {
return add(input1, input2, base);
}
else if (op == '-'){
return subtract(input1, input2, base);
}
}
/*
* Print to "stderr" and exit program
*/
void print_usage(char* name) {
fprintf(stderr, "----------------------------------------------------\n");
fprintf(stderr, "Usage: %s base input1 operation input2\n", name);
fprintf(stderr, "base must be number between 2 and 36, inclusive\n");
fprintf(stderr, "input1 and input2 are arbitrary-length integers\n");
fprintf(stderr, "Two operations are allowed '+' and '-'\n");
fprintf(stderr, "----------------------------------------------------\n");
exit(1);
}
/*
* MAIN: Run the program and tests your functions.
* sample command: ./bignum 4 12 + 13
* Result: 31
*/
int main(int argc, char** argv) {
int input_base;
int* input1;
int* input2;
int* result;
if(argc != 5) {
print_usage(argv[0]);
}
input_base = string_to_integer(argv[1]);
if(!valid_base(input_base)) {
fprintf(stderr, "Invalid base: %s\n", argv[1]);
print_usage(argv[0]);
}
if(!valid_input(argv[2], input_base)) {
fprintf(stderr, "Invalid input1: %s\n", argv[2]);
print_usage(argv[0]);
}
if(!valid_input(argv[4], input_base)) {
fprintf(stderr, "Invalid input2: %s\n", argv[4]);
print_usage(argv[0]);
}
if(argv[3][0] != '-' && argv[3][0] != '+') {
fprintf(stderr, "Invalid operation: %s\n", argv[3]);
print_usage(argv[0]);
}
input1 = string_to_integer_array(argv[2]);
input2 = string_to_integer_array(argv[4]);
result = perform_math(input1, input2, argv[3][0], input_base);
printf("Result: ");
bignum_print(result);
printf("\n");
exit(0);
}
Line 344:
void print_usage(char* name) {
fprintf(stderr, "----------------------------------------------------\n");
fprintf(stderr, "Usage: %s base input1 operation input2\n", name);
fprintf(stderr, "base must be number between 2 and 36, inclusive\n");
fprintf(stderr, "input1 and input2 are arbitrary-length integers\n");
fprintf(stderr, "Two operations are allowed '+' and '-'\n");
fprintf(stderr, "----------------------------------------------------\n");
exit(1);
}
Line 390:
if(argv[3][0] != '-' && argv[3][0] != '+') {
fprintf(stderr, "Invalid operation: %s\n", argv[3]);
print_usage(argv[0]);
}
Now guess why it says "Invalid operation:"...

Converting int to char and reversing

Task is to convert int to char . Here is my code for converting
#include <conio.h>
#include <stdio.h>
main(void)
{
int number,reserve ;
scanf_s("%d",&number);
if (number > 0 || number == 0)
{
do
{
reserve = number % 10;
printf("%c", reserve + '/0');
number /= 10;
} while (number != 0);
}
else
{
number *= -1;
printf("-");
do
{
reserve = number % 10;
printf("%c", reserve + '/0');
number /= 10;
} while (number != 0);
}
_getch();
return 0;
}
The problem is this is printing the result in the reversed of what I want. So I'm searching for a way to reverse it back. However my code is printing char by char. Probably I just need to save all chars into some string but I`m not sure of how do it. Appreciate any help.
Have you tried storing them in an array and printing the array in reverse?
#include <stdio.h>
int main(void)
{
int number,reverse,i ;
scanf("%d",&number);
char string[20];
int index = 0;
if (number< 0){
number *= -1;
printf("-");
}
do
{
reverse = number % 10;
//printf("%c", reverse);
string[index++] = reverse;
number /= 10;
} while (number != 0);
for (i = index ; i >= 0 ; i--)
printf("%c", string[i]);
return 0;
}
As you assumed in your question, you need to store the characters to print them reverse.
Using an array for this task is quite simple:
// I never know how much memory I need ...
char reverse[200];
int i = 0;
reverse[i++] = next calculated character
later
while (i) {
print reverse[--i];
}
This method should suffice in converting an integer to a string without using any standard library functions.
void ReverseString(char *sBuf, int iLength)
{
char *String1 = NULL;
char *String2 = NULL;
for (String1 = sBuf, String2 = sBuf + iLength - 1; String2 > String1; ++String1, --String2)
{
*String1 ^= *String2;
*String2 ^= *String1;
*String1 ^= *String2;
}
}
int YourVeryOwn_itoa(int iINValue, char *sOUTValue)
{
unsigned short writePosition = 0;
if (iINValue < 0)
{
sOUTValue[writePosition++] = '-';
}
do
{
sOUTValue[writePosition++] = (unsigned char)48 + (iINValue % 10);
} while ((iINValue /= 10) > 0);
sOUTValue[writePosition] = '\0';
ReverseString(sOUTValue, writePosition);
return writePosition;
}
void main()
{
char buf[255];
YourVeryOwn_itoa(123456789, buf);
printf("%s", buf);
}

How do I change an array of characters into an array of integers?

More specifically, how do I change'
char tempList[256] = "1 -2 -8 4 5";
into something like this:
int tempListNum[256] = {1, -2, -8, 4, 5};
?
I tried this, but I don't know how to append the array.
for (int j = 0; j < 256; j++)
{
if(TEMPS[j] == 45 && (TEMPS[j+1] >= 48 && TEMPS[j+2] >= 48))
{
numToAppend = ((TEMPS[j+1]-'0')*10 + (TEMPS[j+2]-'0')) * -1;
}
if(TEMPS[j] == 45 && TEMPS[j+1] >= 48)
{
numToAppend = (TEMPS[j+1]-'0') * -1;
}
if(TEMPS[j] >= 48)
{
numToAppend = TEMPS[j]-'0';
}
if(TEMPS[j] >= 48 && TEMPS[j+1] >= 48)
{
numToAppend = TEMPS[j]*10 + TEMPS[j+1];
}
}
you can use strtok with space as separator and then use atoi standard library function
Write a function that goes through tempList char by char:
While it encounters only numbers, it should turn them into an int. If there are more than 1 digit, it should multiply the first digit with 10 and add then add it to the int, etc.
When it encounters a space, it should add the newly created int to tempListNum and skip the space.
When it encounters a minus sign, it should multiply the currently created int with -1.
Repeat these steps until end of list.
You can try this
unsigned char index1 = 0,index2 =0;
int PrevValue = 0;
for(;index1 <= 255;index1 ++)
{
if(tempList[index1] != ' ') //space
{
tempListNum[index2] = PrevValue*10 + (tempList[index1]-0x30);
PrevValue = tempListNum[index2] ;
}
else
{
PrevValue = 0;
index2++;
}
}
You can add further optimizations and other validations.
This isn't terribly difficult. I'd recommend a custom strtoi function instead of atoi since atoi allows for little error handling. For example, atoi("1.3") would return 1 with no other way to get other information like the fact that ".3" makes it not an integer. Moreover, the value 1 is obviously within the range of the int type, so even errno may not be of help.
That is why I write and use strtoi below, which uses the C library's strtol function and has the same behaviors. If you need help understanding what strtoi does, look up documentation for strtol since they're pretty much the same except for the range of valid values (between INT_MIN and INT_MAX, both inclusive, instead of between LONG_MIN and LONG_MAX, both inclusive).
#include <stdio.h>
#include <stdlib.h> //strtol
#include <string.h> //strtok
#include <limits.h> //INT_MIN, INT_MAX
#include <errno.h> //errno, ERANGE
int
strtoi (const char *s, char **rem, int base)
{
long n;
int esave = errno;
errno = 0;
n = strtol (s, rem, base);
if (n < INT_MIN || n > INT_MAX)
errno = ERANGE;
if (errno)
{
if (n == LONG_MAX)
return INT_MAX;
return INT_MIN;
}
errno = esave;
return (int) n;
}
int
main (void)
{
char tempList[256] = "1 -2 -8 4 5";
int tempListNum[128] = {0};
int i, n;
char *listPtr;
char *listPtr2;
listPtr = strtok (tempList, " ");
for (i = 0;
listPtr != NULL && (size_t) i < sizeof tempListNum / sizeof tempListNum[0];
++i)
{
errno = 0;
n = strtoi (listPtr, &listPtr2, 10);
if (*listPtr2)
{
fprintf (stderr, "error: value `%s' is not an integer ... ignoring value\n", listPtr);
--i;
}
// value out of range or some other implementation-defined error
else if (errno)
{
perror (listPtr);
}
listPtr = strtok (NULL, " ");
}
// n is now the number of items in the array
n = i;
for (i = 0; i < n; ++i)
printf ("%d\n", tempListNum[i]);
}
You could obviously avoid strtok and just use strtoi by itself. That might actually be a cleaner solution!
Is this a homework assignment, or maybe a interview question?
I haven't tested this, but you could do something like this:
#define enum _error {
success = 0,
parse_error,
out_of_memory,
invalid_arg,
insuficient_buffer
} error;
error parse_int(
char **pp,
int *val)
{
error e = success;
char neg = 0;
if (!pp || !*pp || !val) {
e = invalid_arg;
goto exit;
}
*val = 0;
while (**pp) {
if (**pp == '-') {
if (val) {
e = parse_error;
goto exit;
}
neg = 1;
}
else if (**pp >= '0' **pp <= '9') {
*val *= 10;
*val += **pp - '0';
}
else {
break;
}
++*pp;
}
if (neg) {
*val *= -1;
}
exit:
return e;
}
error parse_array_of_ints(
char *p,
unsigned int parsed_ints_size,
int **parsed_ints)
{
error e = success;
unsigned int cur = 0;
if (!p || !parsed_ints) {
e = invalid_arg;
goto exit;
}
for (;;) {
while (*p && *p != '-' && *p < '0' && *p > '9') {
p++;
}
if (!*p) {
break;
}
if (cur = parsed_ints_size) {
e = insuficient_buffer;
goto exit;
}
e = parse_int(&p, parsed_ints+cur);
if (e != success) {
goto exit;
}
cur++;
}
exit:
return e;
}
For example, with while loop and functions sscanf and strtok from strings.h.
#include <stdio.h>
#include <string.h>
int main (void)
{
char tempList[256] = "1 -2 -8 4 5";
int tempListNum[256] = {0};
int cnt = 0;
int tmp = 0;
char * str = strtok (tempList," ");
while(str != NULL)
{
if(sscanf(str, "%d", &tmp))
{
tempListNum[cnt] = tmp;
cnt++;
}
// read next number
str = strtok (NULL," ");
}
// test output
for(tmp = 0; tmp < cnt; tmp++)
{
printf("%d ", tempListNum[tmp]);
}
return 0;
}
Tested in Visual Studio 2012.
Consider this short example:
#include <stdio.h>
#include <stdlib.h>
int main ()
{
char tempList[256] = "1 -2 -8 4 5";
int tempListNum[256] = {0};
int numCnt = 0;
char * pEnd = tempList;
int number = 0;
while( strlen(pEnd) > 0)
{
tempListNum[numCnt] = strtol (pEnd,&pEnd,0);
numCnt++;
}
for(number = 0; number < numCnt; number++)
{
printf("%d ", tempListNum[number]);
}
return 0;
}
You can try this :
char tempList[256] = "1 -2 -8 4 5";
int tempListNum[256]
int i=0,k=0;
while(i<256)
{
int num=0;
for(int j=0;tempList[j]!=' ';j++)
{
int neg=0,counter=0; //Not worked in C for a long time and I am not sure if it supports Bools
if(a[j]=='-')
neg=1;
else
num=num*10+int(tempList[j])-int('0');
counter++;
}
if(neg==1)
num=-num;
tempListNum[k]=num;
k++; i+=counter;
}
use this: it works on me.
#include <stdio.h>
#include <ctype.h>
#include <math.h>
int main() {
char tempList[256] = "1 -2 -82 43 5";
char tempNumber[10];
int tempNumberCounter = 0;
int i, j,counter=0, isNegative = 1;
int numTempList[256] = {0};
for(i=0; tempList[i] != '\0'; i++) {
if(tempList[i-1] == '-') {
isNegative = -1;
}
if(isdigit(tempList[i])) {
tempNumber[tempNumberCounter] = tempList[i];
tempNumberCounter++;
}
if(tempList[i] == ' ' || tempList[i+1] == '\0') {
tempNumber[tempNumberCounter] = '\0';
for(j=0; j<strlen(tempNumber); j++) {
numTempList[counter] += (tempNumber[j] - '0') * pow(10, strlen(tempNumber)-j-1);
}
numTempList[counter] *= isNegative;
counter++;
tempNumberCounter = 0;
*tempNumber = 0;
isNegative = 1;
}
}
for(i=0; numTempList[i] != 0; i++) {
printf("%d ", numTempList[i]);
}
return 0;
}
numTempList is the converted array from tempList. this is just a sample implementation of char to int array, you can refactor this into functions so that you can reuse this functionality..
Modified:
Now it can also read multiple digits

C code to convert hex to int

I am writing this code to convert a hex entry into its integer equivalent. So A would be 10 and B would be 11 etc. This code acts weirdly, in that it seg. faults at random locations and including an extra newline character at times will get it to work. I am trying to debug it, just so I can understand what I am doing wrong here. Can anyone take a look and help me here ? Thanks a lot for your time.
/* Fixed working code for anyone interested */
#include <stdio.h>
#include <stdlib.h>
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
int dec;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
if ( temp[i] == '\0')
{
return ;
}
if (temp[i] == '0' || temp[i] == 'x' || temp[i] == 'X' )
{
printf("0");
answer = temp[i];
}
// compare each temp[i] with all contents in hexchar[]
int j;
int a = temp[i];
for ( j=0; hexchar[j] != '\0'; j++)
{
if ( temp[i] == hexchar[j] )
{
answer *= 16;
answer = answer + 10 + (j/2);
// printf("%d\n",answer );
break;
}
}
}
return answer;
}
main()
{
char *test[] =
{ "bad",
"aabbdd"
"0100",
"0x1",
"0XA",
"0X0C0BE",
"abcdef",
"123456",
"0x123456",
"deadbeef",
"zog_c"
};
int answer=0;
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
printf("main():Number of chars = %d\n",numberOfChars);
int i;
// Go through each character and convert Hex to Integers.
for ( i = 0; i<numberOfChars;i++)
{
// Need to take the first char and then go through it and convert
it.
answer = hextoint(test[i]);
printf("%d\n",answer );
}
}
Let's take a look.
unsigned int hextoint(const char temp[])
{
int i;
int answer = 0;
char hexchar[] = "aAbBcCdDeEfF" ;
for ( i=0; temp[i] != '\0'; i++ )
{
printf("In here");
printf("%c\t",temp[i] );
}
return answer;
}
This doesn't seem to even try to do any conversion. It should always return 0, since answer is never assigned any other value. Normally, you'd do something like:
for (i=0; input[i] != '\0'; i++) {
answer *= 16;
answer += digit_value(input[i]);
}
return answer;
Where digit_value (obviously enough) returns the value of an individual digit. One way to do this is:
int digit_value(char input) {
input = tolower(input);
if (input >= '0' && input <= '9')
return input - '0';
if (input >= 'a' && input <= 'f')
return input - 'a' + 10;
return -1; // signal error.
}
Then, looking at main:
main()
{
Depending on the "implicit int" rule is generally poor practice, at least IMO. It's much better to specify the return type.
// Calculate the number of char's.
int numberOfChars;
numberOfChars = sizeof test /sizeof test[0];
This actually calculates the number of strings, not the number of chars.
for ( i = 0; i<=numberOfChars;i++)
Valid subscripts run from 0 through the number of items - 1, so this attempts to read past the end of the array (giving undefined behavior).
This'll work for any number within the unsigned int range, the nice thing is it does not use any other library functions so it is great for micro-controllers where space is tight.
unsigned int hexToInt(const char *hex)
{
unsigned int result = 0;
while (*hex)
{
if (*hex > 47 && *hex < 58)
result += (*hex - 48);
else if (*hex > 64 && *hex < 71)
result += (*hex - 55);
else if (*hex > 96 && *hex < 103)
result += (*hex - 87);
if (*++hex)
result <<= 4;
}
return result;
}
The problem is with calculating numberOfChars part. sizeof test is actually the size of the pointer, not the total length of all characters in your array, so the number returned in your code would be 1, which makes the for loop go to the second index of test (test[1]) which does not have a \0 at the end. Try using strlen for calculating numberOfChars.
This may not me the most optimal method, but it should work without problem.
unsigned int hex_to_int(const char* hex) {
unsigned int result = 0;
size_t len = strlen(hex);
for (size_t i = 0; i < len; ++i) {
char cur_char = tolower(hex[len - i - 1]);
// direct return if encounter any non-hex character.
if (!(isdigit(cur_char) && (cur_char >= 'a' && cur_char <= 'f'));)
return result;
unsigned int char_val = (isdigit(cur_char) ? cur_char - '0' : 10 + cur_char - 'a');
result += round(pow(16, i)) * char_val;
}
return result;
}

Resources