¿Convertir cadena hexadecimal (char []) a int?

82

Tengo un char [] que contiene un valor como "0x1800785" pero la función a la que quiero dar el valor requiere un int, ¿cómo puedo convertir esto en un int? He buscado alrededor pero no puedo encontrar una respuesta. Gracias.

kizyle502
fuente
1
posible duplicado de convertir cadena en int firmado
máscara de bits

Respuestas:

207

¿Usted ha intentado strtol()?

strtol - convierte la cadena en un entero largo

Ejemplo:

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

En caso de que la representación de cadena del número comience con un 0xprefijo, se debe usar 0 como base:

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

(También es posible especificar una base explícita como 16, pero no recomendaría introducir redundancia).

Mahendra Gunawardena
fuente
4
Si la cadena hexagonal siempre se introduce con un "0x"como se indica en la pregunta, debe usar en 0lugar de 16.
Jens Gustedt
16
@KelvinHu No confíes en cplusplus.com, es una mierda. Si hay alguno, vaya a cppreference.com .
1
@KelvinHu En ese sitio hay mucha información técnicamente incorrecta o redactada libremente, y si ve programadores de C ++ alrededor de SO, todos desanimarán la confianza en él por esta razón.
1
@ H2CO3 Bien, ya veo, soy nuevo en C ++, así que busco esto con Google y coloca cplusplus.com en la primera posición. Realmente gracias por tu información.
Kelvin Hu
3
Como nota al margen, strtolle da un tipo de letra longque es genial cuando se trata de un hexágono muy grande. Úselo strtollcomo tipo long longpara hexágonos aún más grandes.
Steven Lu
20

O si desea tener su propia implementación, escribí esta función rápida como ejemplo:

/**
 * 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;
}
radhoo
fuente
1
Muy útil para plataformas con recursos limitados como microcontroladores.
Mubin Icyer
19

Algo como esto podría ser útil:

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

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

Leer man sscanf

saxi
fuente
1
Esto provoca un comportamiento indefinido, %xdebe recibir la dirección de un unsigned int. E incluso si arregla eso, si el número representado por la cadena es mayor que, UINT_MAXentonces es un comportamiento indefinido nuevamente
MM
10

Suponiendo que te refieres a que es una cadena, ¿qué tal strtol ?

James McLaughlin
fuente
Originalmente me había vinculado a strtod (-> double) en lugar de strtol. Supongo que alguien lo vio mientras editaba.
James McLaughlin
Bueno, eso no es un gran error ... el compilador lanza automáticamente el valor de retorno si se alimenta a un int . +1, por cierto.
No creo que un doble pueda almacenar todos los valores posibles que un entero de 32 bits pueda (en realidad, ¿alguien sabe si esto es cierto? No estoy al 100% en la representación de números de punto flotante).
James McLaughlin
4
@JamesMcLaughlin Puede. Un IEEE doble de 64 bits tiene una precisión integral de aproximadamente 2 ^ 53.
Detalle, "Un IEEE doble de 64 bits tiene una precisión integral de aproximadamente 2 ^ 53". y un poco de señal. Entonces puede manejar int54_ty uint53_t.
chux - Reincorporar a Monica
5

Úselo strtolsi tiene libc disponible como sugiere la respuesta principal. Sin embargo, si le gustan las cosas personalizadas o está en un microcontrolador sin libc o algo así, es posible que desee una versión ligeramente optimizada sin ramificaciones complejas.

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

La magia del cambio de bits se reduce a: solo use los últimos 4 bits, pero si no es un dígito, agregue también 9.

Rojo lima
fuente
Si tiene la oportunidad, ¿podría explicar un poco más cómo funciona la línea "v ="? Me las arreglé para implementar su función para mis propios fines de la siguiente manera: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; }
iamoumuamua
Déjame ver si puedo arreglar esto ^^ Supongo que es bastante complicado ... Premisa: '0' - '9' todos comienzan con '0011 ????' en binario. 'A' - 'F' comienzan con '0100 ????' en binario. Podemos usar esta información para evitar ramificaciones. (c & 0xF) → solo los últimos cuatro bits contienen el valor. Este ya es el valor correcto para '0' - '9'. En el caso de 'A' - 'F', debemos agregar un decimal 9 para obtener el valor correcto. (Hex C, por ejemplo, termina en 0011. Que es 3. Pero queremos 12. Por lo tanto, debemos sumar 9. (c >> 6) | ((c >> 3) & 0x8)→ se evalúa como 9 en el caso de una letra, o 0 en el caso de un decimal. Sí, bastante hacky :)
LimeRed
3

Pruebe el siguiente bloque de código, funciona para mí.

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

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

La salida es:

value x: 820 - 2080
Cave el código
fuente
3

Entonces, después de un tiempo de búsqueda y descubrir que strtol es bastante lento, he codificado mi propia función. Solo funciona para mayúsculas en letras, pero agregar funcionalidad en minúsculas no es un problema.

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

Uso:

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

1 porque el hexadecimal que queremos convertir comienza en el desplazamiento 1, y 8 porque es la longitud hexadecimal.

Speedtest (1.000.000 de llamadas):

strtol ~ 0.4400s
hexToInt ~ 0.1100s
mexikanoZ
fuente
2

Una solución rápida y sucia:

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

Debe asegurarse de que su entrada sea correcta, sin validación incluida (se podría decir que es C). Menos mal que es bastante compacto, funciona con 'A' a 'F' y 'a' a 'f'.

El enfoque se basa en la posición de los caracteres del alfabeto en la tabla ASCII, echemos un vistazo, por ejemplo, a Wikipedia ( https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png ). En pocas palabras, los números están debajo de los caracteres, por lo que los caracteres numéricos (0 a 9) se convierten fácilmente restando el código por cero. Los caracteres alfabéticos (de la A a la F) se leen poniendo a cero los tres últimos bits (lo que hace que funcione efectivamente con mayúsculas o minúsculas), restando uno (porque después del enmascaramiento de bits, el alfabeto comienza en la posición uno) y sumando diez ( porque de la A a la F representan el valor 10 al 15 en código hexadecimal). Finalmente, necesitamos combinar los dos dígitos que forman el nibble inferior y superior del número codificado.

Aquí vamos con el mismo enfoque (con variaciones menores):

#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;
}
Simón
fuente
1
Después de publicar la respuesta, descubrí que me perdí la publicación de @LimeRed, que es muy linda
Simon
1

Esta es una función para convertir directamente una matriz de caracteres hexadecimal que contiene un número entero que no necesita una biblioteca adicional:

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;
}
Vaibhav VK
fuente
0

Hice una biblioteca para hacer una conversión hexadecimal / decimal sin el uso de stdio.h. Muy simple de usar:

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

Antes de la primera conversión, inicialice la matriz utilizada para la conversión con:

void init_hexdec ();

Aquí el enlace en github: https://github.com/kevmuret/libhex/

kevmuret
fuente
0

He hecho algo similar, creo que podría ayudarte, en realidad funciona para mí.

int main(){ int co[8],i;char ch[8];printf("please enter the string:");scanf("%s",ch);for(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;}}

aquí solo he tomado una cadena de 8 caracteres. Si quieres, puedes agregar una lógica similar para 'a' a 'f' para dar sus valores hexadecimales equivalentes, no lo he hecho porque no lo necesitaba.

sairam singh
fuente
-1

Utilice xtoi (stdlib.h). La cadena tiene "0x" como dos primeros índices, así que recorta val [0] y val [1] enviando xtoi & val [2].

xtoi( &val[2] );

Burstfulovic
fuente
xtoino es una función estándar
MM
-5

Sé que esto es muy antiguo, pero creo que las soluciones parecían demasiado complicadas. Prueba esto en 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 
CHEWBAKKEE
fuente