2010-09-24 63 views
11

Digamos que tiene una cadena como:¿Cómo convertir una cadena de valores hexadecimales a una cadena?

string hex = "48656c6c6f"; 

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

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

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

+0

asociado, véase [Conversión de cadena hexadecimal con "0x" inicial a corto firmado en C++?] (Http://stackoverflow.com/q/1487440/608639) – jww

Respuesta

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

Me gustaría ir con esta respuesta, ya que no dependerá de las longitudes enteras – Xzhsh

+0

Almacenando una longitud en un 'int'. Ahora, ¿por qué harías eso? – sbi

+1

@sbi: Si no lo hiciera, llamaría a string :: length() cada vez que pase el ciclo. Como sé que va a permanecer constante, no es necesario pasar por el trabajo extra. (A menos que esté cuestionando mi elección de int sobre decir mucho tiempo - porque no pude ver esto como práctico en una cadena más larga que la que cabría en una longitud int) –

1

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

+0

No necesita el 0x, solo necesita pasar 16 como el tercer argumento. Pero strtol es un exceso masivo para este trabajo ... – zwol

+0

Ah, ya veo. Sí, solo quería dar una respuesta rápida, esta no es, por supuesto, la mejor solución (especialmente para C++). – schnaader

16

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

// 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 puede preguntar razonablemente por qué se podría hacer de esta manera cuando hay strtol, y su uso es significativamente menos código (como en la respuesta de James Curran). Bueno, ese enfoque es un orden decimal completo de magnitud más lento, porque copia cada fragmento de dos bytes (posiblemente asignando memoria de pila para hacerlo) y luego invoca una rutina general de conversión de texto a número que no se puede escribir de manera eficiente como el código especializado de arriba. El enfoque de Christian (usando istringstream) es cinco veces más lento que que. Aquí hay una trama de referencia: puedes notar la diferencia incluso con un pequeño bloque de datos para decodificar, y se hace evidente a medida que las diferencias se agrandan. (Tenga en cuenta que ambos ejes están en una escala logarítmica.)

Benchmark comparison plot

¿Es esta optimización prematura? Diablos no Este es el tipo de operación que se empuja en una rutina de biblioteca, olvidada y luego llamada miles de veces por segundo. Necesita gritar. Trabajé en un proyecto hace unos años que hizo un uso intensivo de las sumas de comprobación SHA1 internamente: obtuvimos entre 10 y 20% de aceleraciones en operaciones comunes al almacenarlas como bytes sin procesar en lugar de hexadecimales, convirtiendo solo cuando teníamos que mostrarlas a la usuario, y eso fue con las funciones de conversión que ya se habían ajustado a la muerte. Uno podría honestamente preferir la brevedad al rendimiento aquí, dependiendo de cuál es la tarea más grande, pero si es así, ¿por qué diablos estás 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 sobre lo que la computadora tiene que hacer.

+0

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

+1

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

+1

+1 para enfocarse en el rendimiento, ya que es probable que se use con frecuencia, como en un ciclo de rendimiento crítico. También para darse cuenta de que no es la respuesta "correcta" del libro de texto, y recomendar esconderlo detrás de una función de biblioteca. Ese es el mejor lugar para un código tan feo como este: detrás de una bonita interfaz. –

3
std::string str("48656c6c6f"); 
std::string res; 
res.reserve(str.size()/2); 
for (int i = 0; i < str.size(); i += 2) 
{ 
    std::istringstream iss(str.substr(i, 2)); 
    int temp; 
    iss >> std::hex >> temp; 
    res += static_cast<char>(temp); 
} 
std::cout << res; 
+0

Podría preasignar, ¡la longitud se conoce de antemano! –

+0

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

+0

@Zack: edité mi respuesta y preasigné el tamaño de la cadena de salida como mencionó Caron y usted ya lo hizo. Me interesa ¿cuáles son las diferencias de rendimiento entre mi solución y la tuya? –

5

que no puedo comentar, pero la solución de Zwol tiene un error:

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

es correcta

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

como el operador de desplazamiento tiene menor prioridad que añadir

+0

Editado para incluir esto, gracias! – ti7

+0

Gracias por señalar este error. Me avergüenza no haberlo notado, y he corregido mi respuesta. (el intento de ti7 fue rechazado por personas que seguían las reglas de forma un tanto literal). – zwol

+0

Lamentablemente, sin embargo, solo * he escuchado * sobre esto cuando ti7 intentó editar mi respuesta, no recibimos notificaciones de otras respuestas a una pregunta. Esta es la razón por la que los comentarios hubieran sido mejores si hubiera podido. – zwol

Cuestiones relacionadas