Convert hex string (char []) to int?
Asked Answered
R

15

107

I have a char[] that contains a value such as "0x1800785" but the function I want to give the value to requires an int, how can I convert this to an int? I have searched around but cannot find an answer. Thanks.

Remodel answered 14/4, 2012 at 18:58 Comment(2)
possible duplicate of convert string into signed intIsom
use strtol(), with base=16, the rest of how to use it is here: https://mcmap.net/q/49634/-correct-usage-of-strtolBaucis
T
257

Have you tried strtol()?

strtol - convert string to a long integer

Example:

const char *hexstring = "abcdef0";
int number = (int)strtol(hexstring, NULL, 16);

In case the string representation of the number begins with a 0x prefix, one must should use 0 as base:

const char *hexstring = "0xabcdef0";
int number = (int)strtol(hexstring, NULL, 0);

(It's as well possible to specify an explicit base such as 16, but I wouldn't recommend introducing redundancy.)

Turbojet answered 14/4, 2012 at 19:1 Comment(9)
If the hexstring is always introduced by a "0x" as given in the question you should just use 0 instead of 16.Celibacy
As a side note strtol gives you type long which is great when dealing with a very large hex. Use strtoll for type long long for even larger hexes.Unriddle
Noticed a surprising discrepancy between "begins with a 0x prefix, one must use 0 as base" and C11 §7.22.1.4 "If the value of base is 16, the characters 0x or 0X may optionally precede the sequence of letters and digits" as in printf("%ld\n", strtol("0x12", 0, 16));. You may wish to review this.Declan
@chux Changed "must" to "should", as it's not obligatory, but better practice, to let the function detect the base (it eliminates a tiny bit of redundancy).Turbojet
Technically, you should use strtoul instead of strtol, because it converts it to unsigned, which means you won't get negative outputs from 8-digit hex values. This stumped me for quite awhile! See en.cppreference.com/w/c/string/byte/strtoulRecorder
In case of const char *hexstring = "0xabcdef00";, 8 characters, it return 0x7fffffff due to value overflow. Use strtoll() when you deal with unsigned int value.`Stob
On the other hand, strtoull will return unexpected results with negative hex numbers. And it will give you no error.Shrinkage
Also see https://mcmap.net/q/205118/-detecting-strtol-failure-duplicate/547270 and https://mcmap.net/q/49634/-correct-usage-of-strtol/547270.Chopping
https://mcmap.net/q/49634/-correct-usage-of-strtolBaucis
M
32

Or if you want to have your own implementation, I wrote this quick function as an example:

/**
 * hex2int
 * take a hex string and convert it to a 32bit number (max 8 hex digits)
 */
uint32_t hex2int(char *hex) {
    uint32_t val = 0;
    while (*hex) {
        // get current character then increment
        uint8_t byte = *hex++; 
        // transform hex character to the 4bit equivalent number, using the ascii table indexes
        if (byte >= '0' && byte <= '9') byte = byte - '0';
        else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;    
        // shift 4 to make space for new digit, and add the 4 bits of the new digit 
        val = (val << 4) | (byte & 0xF);
    }
    return val;
}
Mourning answered 20/8, 2016 at 10:19 Comment(1)
Very useful for platforms with limited resources like microcontrollers.Robynroc
T
26

Something like this could be useful:

char str[] = "0x1800785";
int num;

sscanf(str, "%x", &num);
printf("0x%x %i\n", num, num); 

Read man sscanf

Telpher answered 14/4, 2012 at 19:14 Comment(1)
This causes undefined behaviour, %x must receive the address of an unsigned int. And even if you fix that, if the number represented by the string is larger than UINT_MAX then it is undefined behaviour againNationality
P
12

Use strtol if you have libc available like the top answer suggests. However if you like custom stuff or are on a microcontroller without libc or so, you may want a slightly optimized version without complex branching.

#include <inttypes.h>


/**
 * xtou64
 * Take a hex string and convert it to a 64bit number (max 16 hex digits).
 * The string must only contain digits and valid hex characters.
 */
uint64_t xtou64(const char *str)
{
    uint64_t res = 0;
    char c;

    while ((c = *str++)) {
        char v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8);
        res = (res << 4) | (uint64_t) v;
    }

    return res;
} 

The bit shifting magic boils down to: Just use the last 4 bits, but if it is a non digit, then also add 9.

Pythia answered 19/7, 2019 at 12:34 Comment(1)
If you get a chance could you elaborate a bit more on how the "v =" line works? I managed to implement your function for my own purposes as follows: unsigned long long int hextou64(char *str) { unsigned long long int n = 0; char c, v; for (unsigned char i = 0; i < 16; i++) { c = *(str + i); v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8); n = (n << 4) | (unsigned long long int)v; } return n; }Skylark
U
11

Assuming you mean it's a string, how about strtol?

Unlimited answered 14/4, 2012 at 19:1 Comment(5)
I'd originally linked to strtod (-> double) instead of strtol. I guess someone saw it while I was editing.Unlimited
Well, that's not much of a mistake... the compiler auto-casts the return value if fed to an int. +1, btw.Turbojet
I don't think a double can store all possible values a 32-bit integer can (actually, does anyone know if this is true? I'm not 100% on floating point number representation.)Unlimited
@JamesMcLaughlin It can. A 64-bit IEEE double has integral-accuracy to about 2^53.Seagoing
Detail, "A 64-bit IEEE double has integral-accuracy to about 2^53." and a sign bit. So it can handle int54_t and uint53_t.Declan
F
7

One quick & dirty solution:

// makes a number from two ascii hexa characters
int ahex2int(char a, char b){

    a = (a <= '9') ? a - '0' : (a & 0x7) + 9;
    b = (b <= '9') ? b - '0' : (b & 0x7) + 9;

    return (a << 4) + b;
}

You have to be sure your input is correct, no validation included (one could say it is C). Good thing it is quite compact, it works with both 'A' to 'F' and 'a' to 'f'.

The approach relies on the position of alphabet characters in the ASCII table, let's peek e.g. to Wikipedia (https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png). Long story short, the numbers are below the characters, so the numeric characters (0 to 9) are easily converted by subtracting the code for zero. The alphabetic characters (A to F) are read by zeroing other than last three bits (effectively making it work with either upper- or lowercase), subtracting one (because after the bit masking, the alphabet starts on position one) and adding ten (because A to F represent 10th to 15th value in hexadecimal code). Finally, we need to combine the two digits that form the lower and upper nibble of the encoded number.

Here we go with same approach (with minor variations):

#include <stdio.h>

// takes a null-terminated string of hexa characters and tries to 
// convert it to numbers
long ahex2num(unsigned char *in){

    unsigned char *pin = in; // lets use pointer to loop through the string
    long out = 0;  // here we accumulate the result

    while(*pin != 0){
        out <<= 4; // we have one more input character, so 
                   // we shift the accumulated interim-result one order up
        out +=  (*pin < 'A') ? *pin & 0xF : (*pin & 0x7) + 9; // add the new nibble
        pin++; // go ahead
    }

    return out;
}

// main function will test our conversion fn
int main(void) {

    unsigned char str[] = "1800785";  // no 0x prefix, please
    long num;

    num = ahex2num(str);  // call the function

    printf("Input: %s\n",str);  // print input string
    printf("Output: %x\n",num);  // print the converted number back as hexa
    printf("Check: %ld = %ld \n",num,0x1800785);  // check the numeric values matches

    return 0;
}
Friedrich answered 6/10, 2019 at 0:35 Comment(1)
After posting the answer I found out I missed the @Pythia 's post, which is very cuteFriedrich
H
4

Try below block of code, its working for me.

char p[] = "0x820";
uint16_t intVal;
sscanf(p, "%x", &intVal);

printf("value x: %x - %d", intVal, intVal);

Output is:

value x: 820 - 2080
Hitherward answered 5/9, 2014 at 8:41 Comment(0)
B
3

So, after a while of searching, and finding out that strtol is quite slow, I've coded my own function. It only works for uppercase on letters, but adding lowercase functionality ain't a problem.

int hexToInt(PCHAR _hex, int offset = 0, int size = 6)
{
    int _result = 0;
    DWORD _resultPtr = reinterpret_cast<DWORD>(&_result);
    for(int i=0;i<size;i+=2)
    {
        int _multiplierFirstValue = 0, _addonSecondValue = 0;

        char _firstChar = _hex[offset + i];
        if(_firstChar >= 0x30 && _firstChar <= 0x39)
            _multiplierFirstValue = _firstChar - 0x30;
        else if(_firstChar >= 0x41 && _firstChar <= 0x46)
            _multiplierFirstValue = 10 + (_firstChar - 0x41);

        char _secndChar = _hex[offset + i + 1];
        if(_secndChar >= 0x30 && _secndChar <= 0x39)
            _addonSecondValue = _secndChar - 0x30;
        else if(_secndChar >= 0x41 && _secndChar <= 0x46)
            _addonSecondValue = 10 + (_secndChar - 0x41);

        *(BYTE *)(_resultPtr + (size / 2) - (i / 2) - 1) = (BYTE)(_multiplierFirstValue * 16 + _addonSecondValue);
    }
    return _result;
}

Usage:

char *someHex = "#CCFF00FF";
int hexDevalue = hexToInt(someHex, 1, 8);

1 because the hex we want to convert starts at offset 1, and 8 because it's the hex length.

Speedtest (1.000.000 calls):

strtol ~ 0.4400s
hexToInt ~ 0.1100s
Beagle answered 5/1, 2018 at 17:32 Comment(0)
A
1

I have done a similar thing before and I think this might help you.

The following works for me:

 int main(){
      int co[8];
      char ch[8];
      printf("please enter the string:");
      scanf("%s", ch);
      for (int i=0; i<=7; i++) {
         if ((ch[i]>='A') && (ch[i]<='F')) {
            co[i] = (unsigned int) ch[i]-'A'+10;
         } else if ((ch[i]>='0') && (ch[i]<='9')) {
            co[i] = (unsigned int) ch[i]-'0'+0;
      }
    }

Here, I have only taken a string of 8 characters. If you want you can add similar logic for 'a' to 'f' to give their equivalent hex values. Though, I haven't done that because I didn't need it.

Ahmedahmedabad answered 2/7, 2016 at 5:14 Comment(0)
C
1

This is a function to directly convert hexadecimal containing char array to an integer which needs no extra library:

int hexadecimal2int(char *hdec) {
    int finalval = 0;
    while (*hdec) {
        
        int onebyte = *hdec++; 
        
        if (onebyte >= '0' && onebyte <= '9'){onebyte = onebyte - '0';}
        else if (onebyte >= 'a' && onebyte <='f') {onebyte = onebyte - 'a' + 10;}
        else if (onebyte >= 'A' && onebyte <='F') {onebyte = onebyte - 'A' + 10;}  
        
        finalval = (finalval << 4) | (onebyte & 0xF);
    }
    finalval = finalval - 524288;
    return finalval;
}
Correlate answered 21/7, 2020 at 20:10 Comment(0)
V
0

I made a librairy to make Hexadecimal / Decimal conversion without the use of stdio.h. Very simple to use :

unsigned hexdec (const char *hex, const int s_hex);

Before the first conversion intialize the array used for conversion with :

void init_hexdec ();

Here the link on github : https://github.com/kevmuret/libhex/

Verticillaster answered 3/5, 2014 at 13:41 Comment(0)
B
0

I like @radhoo solution, very efficient on small systems. One can modify the solution for converting the hex to int32_t (hence, signed value).

/**
* hex2int
* take a hex string and convert it to a 32bit number (max 8 hex digits)
*/
int32_t hex2int(char *hex) {
    uint32_t val = *hex > 56 ? 0xFFFFFFFF : 0;
    while (*hex) {
        // get current character then increment
        uint8_t byte = *hex++; 
        // transform hex character to the 4bit equivalent number, using the ascii table indexes
        if (byte >= '0' && byte <= '9') byte = byte - '0';
        else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;    
        // shift 4 to make space for new digit, and add the 4 bits of the new digit 
        val = (val << 4) | (byte & 0xF);
        }
        return val;
    }

Note the return value is int32_t while val is still uint32_t to not overflow.

The

 uint32_t val = *hex > 56 ? 0xFFFFFFFF : 0;

is not protected against malformed string.

Bewley answered 17/8, 2021 at 12:52 Comment(0)
R
0

Here is a solution building upon "sairam singh"s solution. Where that answer is a one to one solution, this one combines two ASCII nibbles into one byte.

// Assumes input is null terminated string.
//
//    IN                       OUT
// --------------------   --------------------
//  Offset  Hex  ASCII         Offset Hex
//  0       0x31 1             0      0x13
//  1       0x33 3                    
//  2       0x61 A             1      0xA0
//  3       0x30 0                    
//  4       0x00 NULL          2      NULL

int convert_ascii_hex_to_hex2(char *szBufOut, char *szBufIn) {

    int i = 0;  // input buffer index
    int j = 0;  // output buffer index
    char a_byte;

    // Two hex digits are combined into one byte
    while (0 != szBufIn[i]) {

        // zero result
        szBufOut[j] = 0;

        // First hex digit
        if ((szBufIn[i]>='A') && (szBufIn[i]<='F')) {
            a_byte = (unsigned int) szBufIn[i]-'A'+10;
        } else if ((szBufIn[i]>='a') && (szBufIn[i]<='f')) {
            a_byte  = (unsigned int) szBufIn[i]-'a'+10;
        } else if ((szBufIn[i]>='0') && (szBufIn[i]<='9')) {
            a_byte  = (unsigned int) szBufIn[i]-'0';
        } else {
            return -1;  // error with first digit
        }
        szBufOut[j] = a_byte << 4;

        // second hex digit
        i++;
        if ((szBufIn[i]>='A') && (szBufIn[i]<='F')) {
            a_byte = (unsigned int) szBufIn[i]-'A'+10;
        } else if ((szBufIn[i]>='a') && (szBufIn[i]<='f')) {
            a_byte  = (unsigned int) szBufIn[i]-'a'+10;
        } else if ((szBufIn[i]>='0') && (szBufIn[i]<='9')) {
            a_byte  = (unsigned int) szBufIn[i]-'0';
        } else {
            return -2;  // error with second digit
        }
        szBufOut[j] |= a_byte;

        i++;
        j++;
    }
    szBufOut[j] = 0;
    return 0;  // normal exit
}

Resonance answered 10/9, 2021 at 13:38 Comment(1)
Here is my solution to convert a hex into an integer. Very simple! To handle an array of Hex one can use a 'for loop'. #include<stdio.h> int main (void) { unsigned char t=0x8A; int c = (t>>4 )*16 + (t & 0x0F); printf("The integrate value of 0X8A is : %d\n", c);Shirlshirlee
S
-2

Here is my solution to convert a hex into an integer. Very simple! To handle an array of Hex one can use a 'for loop'.

#include<stdio.h>
int main (void)
{
     unsigned char t=0x8A;
     int c = (t>>4 )*16 + (t & 0x0F);
     printf("The integrate value of 0X8A is : %d\n", c);
}

The value should be 138.

Shirlshirlee answered 20/5, 2023 at 4:38 Comment(1)
That's not what was asked. This code starts off with an integer, shifts off 4 bits, then adds those bits back in.... it does nothingFourinhand
W
-7

I know this is really old but I think the solutions looked too complicated. Try this in VB:

Public Function HexToInt(sHEX as String) as long
Dim iLen as Integer
Dim i as Integer 
Dim SumValue as Long 
Dim iVal as long
Dim AscVal as long

    iLen = Len(sHEX)
    For i = 1 to Len(sHEX)
      AscVal = Asc(UCase(Mid$(sHEX, i,  1)))
      If AscVal >= 48 And AscVal  <= 57 Then
        iVal  = AscVal - 48
      ElseIf AscVal >= 65 And AscVal <= 70 Then
        iVal = AscVal - 55
      End If 
      SumValue = SumValue + iVal * 16 ^ (iLen- i)
    Next i 
    HexToInt  = SumValue
End Function 
Wittgenstein answered 21/6, 2018 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.