¿Cómo convertir una cadena de valores hexadecimales en una cadena?

6 minutos de lectura

Digamos que tengo una cadena como:

string hex = "48656c6c6f";

Donde cada dos caracteres corresponden a la representación hexadecimal de su valor ASCII, por ejemplo:

0x48 0x65 0x6c 0x6c 0x6f = "Hello"

Entonces, ¿cómo puedo obtener "hello" desde "48656c6c6f" sin tener que crear una tabla ASCII de búsqueda? atoi() obviamente no funcionará aquí.

  • Relacionado, consulte ¿Convertir una cadena hexadecimal con “0x” inicial en abreviatura con signo en C++?

    – jww

    07 may. 17 en 04:54

¿Como convertir una cadena de valores hexadecimales en una cadena
zwol

Los dígitos hexadecimales son muy fáciles de convertir a binarios:

// C++98 guarantees that '0', '1', ... '9' are consecutive.
// It only guarantees that 'a' ... 'f' and 'A' ... 'F' are
// in increasing order, but the only two alternative encodings
// of the basic source character set that are still used by
// anyone today (ASCII and EBCDIC) make them consecutive.
unsigned char hexval(unsigned char c)
{
    if ('0' <= c && c <= '9')
        return c - '0';
    else if ('a' <= c && c <= 'f')
        return c - 'a' + 10;
    else if ('A' <= c && c <= 'F')
        return c - 'A' + 10;
    else abort();
}

Así que para hacer toda la cadena se ve algo como esto:

void hex2ascii(const string& in, string& out)
{
    out.clear();
    out.reserve(in.length() / 2);
    for (string::const_iterator p = in.begin(); p != in.end(); p++)
    {
       unsigned char c = hexval(*p);
       p++;
       if (p == in.end()) break; // incomplete last digit - should report error
       c = (c << 4) + hexval(*p); // + takes precedence over <<
       out.push_back(c);
    }
}

Usted podría preguntarse razonablemente por qué uno lo haría de esta manera cuando hay strtol, y usarlo es significativamente menos código (como en la respuesta de James Curran). Bueno, ese enfoque es un orden de magnitud decimal completo más lento, porque copia cada fragmento de dos bytes (posiblemente asignando memoria de montón para hacerlo) y luego invoca una rutina general de conversión de texto a número que no se puede escribir tan eficientemente como el código especializado anterior. El enfoque de Christian (usando istringstream) es cinco veces más lento que ese. Aquí hay un gráfico de referencia: puede notar la diferencia incluso con un pequeño bloque de datos para decodificar, y se vuelve evidente a medida que las diferencias aumentan. (Tenga en cuenta que ambos ejes están en una escala logarítmica).

Gráfico de comparación de puntos de referencia

¿Es esta una optimización prematura? Diablos no. Este es el tipo de operación que se mete en una rutina de biblioteca, se olvida y luego se llama miles de veces por segundo. Necesita gritar. Trabajé en un proyecto hace unos años que hacía un uso intensivo de las sumas de verificación SHA1 internamente: obtuvimos entre un 10 y un 20 % de aceleración en operaciones comunes almacenándolas como bytes sin procesar en lugar de hexadecimales, convirtiendo solo cuando teníamos que mostrárselas al usuario, y eso fue con funciones de conversión que ya habían sido ajustadas hasta la saciedad. Honestamente, uno podría preferir la brevedad al rendimiento aquí, dependiendo de cuál sea la tarea más grande, pero si es así, ¿por qué demonios está codificando en C ++?

Además, desde una perspectiva pedagógica, creo que es útil mostrar ejemplos codificados a mano para este tipo de problema; revela más acerca de lo que la computadora tiene que hacer.

  • -1: instalaciones de biblioteca estándar ignoradas. Le quitaría un punto extra porque se mencionaron en publicaciones anteriores.

    – André Carón

    24 sep.

  • Los ignoré porque son diez veces más lentos que hacerlo a mano. Ver editar.

    – zwol

    24 sep.

  • +1 por centrarse en el rendimiento, ya que es probable que esto sea algo que se use con frecuencia, como en un bucle crítico para el rendimiento. También por darse cuenta de que no es la respuesta de libro de texto “correcta” y recomendar ocultarla detrás de una función de biblioteca. Ese es el mejor lugar para un código feo como este: detrás de una bonita interfaz.

    usuario439793

    25 sep.

  • +1 para el punto de referencia; aunque creo que las implementaciones de James y Christian están bien para donde voy a usar esto.

    – NullUserException

    27 sep.

¿Como convertir una cadena de valores hexadecimales en una cadena
james curran

int len = hex.length();
std::string newString;
for(int i=0; i< len; i+=2)
{
    std::string byte = hex.substr(i,2);
    char chr = (char) (int)strtol(byte.c_str(), null, 16);
    newString.push_back(chr);
}

  • Iría con esta respuesta, ya que no dependerá de longitudes enteras

    – Xzhsh

    24 sep.

  • Almacenar una longitud en un int. Ahora, ¿por qué harías eso?

    – sbi

    24 sep.

  • @sbi: si no lo hiciera, llamaría a string::length() cada vez que pasa por el ciclo. Como sé que se mantendrá constante, no es necesario realizar el trabajo adicional. (A menos que esté cuestionando mi elección de int sobre digamos long, porque no pude ver esto como práctico en una cadena más larga que la que encajaría en una longitud int)

    – James Curran

    24 sep.

  • @James es string::length() O(1)?

    – NullUserException

    24 sep.

  • Estoy de acuerdo con sbi: use size_t para esto. Su IDE/compilador debería marcar esto como una advertencia de todos modos.

    usuario439793

    25 sep.

No puedo comentar, pero la solución de zwol tiene un error:

c = c << 4 + hexval(*p);

es correctamente

c = (c << 4) + hexval(*p);

ya que el operador de cambio tiene menor precedencia que agregar

  • Gracias por señalar este error. Estoy avergonzado de no haberlo notado yo mismo, y he corregido mi respuesta. (El intento de ti7 de hacerlo fue rechazado por personas que seguían las reglas demasiado literalmente).

    – zwol

    10 mar. 16 a las 20:34

  • Lamentablemente, sin embargo, sólo Escuchó sobre esto justo ahora cuando ti7 intentó editar mi respuesta: no recibimos notificaciones de otras respuestas en una pregunta. Es por eso que comentar hubiera sido mejor, si pudieras.

    – zwol

    10 mar. 16 a las 20:35

strtol debería hacer el trabajo si agrega 0x a cada par de dígitos hexadecimales.

  • ¡Podría preasignarse, la longitud se conoce de antemano!

    – André Carón

    24 sep.

  • Odio decirlo, pero esto es cinco veces más lento que la respuesta aceptada (que es diez veces más lenta que mi respuesta).

    – zwol

    24 sep.

  • @Zack: edité mi respuesta y asigné previamente el tamaño de la cadena de salida como mencionó Caron y ya lo hiciste. Me interesa, ¿cuáles son ahora las diferencias de rendimiento entre mi solución y la suya?

    – Christian Ammer

    25 sep.

  • Mi programa de referencia preasignó la cadena de salida para los tres casos; la única diferencia era el código dentro del bucle for.

    – zwol

    25 sep.

.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad