[c++] Conversión de datos binarios a hex imprimible


3 Answers

Mi principal comentario al respecto es que es muy difícil de leer.

Especialmente:

*outit = ((( (*it > '9' ? *it - 0x07 : *it)  - 0x30) << 4) & 0x00f0) + 
            (((*(it+1) > '9' ? *(it+1) - 0x07 : *(it+1)) - 0x30) & 0x000f)

A mi cerebro le tomaría un tiempo asimilar eso y molestarme si heredé el código.

Question

En este hilo, alguien comentó que el siguiente código solo debería usarse en proyectos de 'juguetes'. Desafortunadamente, él no ha regresado para decir por qué no es de calidad de producción, así que esperaba que alguien en la comunidad pudiera asegurarme que el código está bien (porque me gusta bastante) o identificar qué está mal.

template< class T1, class T2>
void hexascii( T1& out, const T2& in )
{
    out.resize( in.size() * 2 );
    const char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    T1::iterator outit = out.begin();
    for( T2::const_iterator it = in.begin(); it != in.end(); ++it )
    {
        *outit++ = hexDigits[*it >> 4];
        *outit++ = hexDigits[*it & 0xF];
    }
}

template<class T1, class T2>
void asciihex( T1& out, const T2& in )
{
    size_t size = in.size;
    assert( !(size % 2) );

    out.resize( size / 2 );
    T1::iterator outit = out.begin();
    for( T2::const_iterator it = in.begin(); it != in.end(); it += 2, ++outit )
    {
    *outit = ((( (*it > '9' ? *it - 0x07 : *it)  - 0x30) << 4) & 0x00f0) + 
                (((*(it+1) > '9' ? *(it+1) - 0x07 : *(it+1)) - 0x30) & 0x000f);
    }
}

Editar: Gracias por su ayuda chicos, han hecho algunas mejoras importantes. He escrito funciones en los dos estilos sugeridos de sus respuestas. Algunas pruebas preliminares sugieren que el segundo método es marginalmente más rápido que el primero, pero el IMO se ve superado por la legibilidad mejorada del primero.

template<class T1>
void asciihex2( T1& out, const std::string& in )
{
    dassert( sizeof(T1::value_type)==1 );
    size_t size = in.size();
assert( !(size % 2) );
    out.resize( size / 2 );
    T1::iterator outit = out.begin();
    for( size_t i = 0; i < in.size(); i += 2 )
    {
        int tmp;
        sscanf( in.c_str() + i, "%02X", &tmp );
        *outit++ = tmp;
    }
}

template<class T1>
void asciihex3( T1& out, const std::string& in )
{
    dassert( sizeof(T1::value_type)==1 );
    size_t size = in.size();
assert( !(size % 2) );
    out.resize( size / 2 );
    T1::iterator outit = out.begin();
const char hexDigits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
for( std::string::const_iterator it = in.begin(); it != in.end(); it += 2, ++outit )
    {
    *outit = (hexDigits[(*it - 0x30) & 0x1f] << 4) + 
              hexDigits[((*(it+1) - 0x30) & 0x1f)];
    }
}

Algunas de las suposiciones que rodean este código: 1: no están pensadas como genéricas, sino que se usan en un espacio de nombre anónimo para traducir datos para una clase específica. 2: Se requiere la creación de plantillas ya que se están utilizando dos tipos de contenedores separados (uno siendo std :: vector, y el otro un contenedor de tipo de matriz de bytes similar de una biblioteca de terceros. 3: El propósito es poder convertir datos binarios de indeterminado longitud en cadenas y viceversa (0x1234abcd <-> "1234abcd") 4: cometer errores de trampas tanto en los modos de depuración como de liberación 5: cuando estas funciones se llaman el tamaño de la cadena ya habrá sido verificado, se usa assert para terminar el procesamiento si algo serio ha salido mal 6: Necesita algunos comentarios

Cualquier otra idea apreciada.




Realmente no me opongo. Es genérico (dentro de los límites), usa constelaciones, referencias cuando es necesario, etc. Falta documentación y la asignación de *outit asciihex *outit no es muy clara a primera vista.

resize el resize inicializa los elementos de salida innecesarios (use reserve lugar).

Tal vez la genérico es algo demasiado flexible: puedes alimentar los algoritmos con cualquier tipo de datos que desees, mientras que solo deberías darle números hexadecimales (no por ejemplo, un vector de double )

Y de hecho, puede ser un poco exagerado , dada la presencia de buenas funciones de la biblioteca.




  • El código tiene declaraciones de afirmación en lugar de un manejo correcto de una condición de error (y si su afirmación está desactivada, el código puede explotar)

  • para el ciclo tiene un doble aumento peligroso del iterador (it + = 2). Especialmente en caso de que su afirmación no se dispare. ¿Qué sucede cuando tu iterador ya está al final y tú ++?

  • El código está modelado, pero lo que estás haciendo es simplemente convertir personajes en números o al revés. Es programación de culto a la carga . Esperas que las bendiciones de la programación de plantillas te lleguen mediante el uso de plantillas. Incluso etiquetó esto como una pregunta de plantilla aunque el aspecto de la plantilla es completamente irrelevante en sus funciones.

  • el * outit = línea es demasiado complicado.

  • el código reinventa la rueda. A lo grande.




Problemas que veo:

hexascii no comprueba si sizeof(T2::value_type)==1

hexascii it desclasifica dos veces, asciihex aún más. No hay ninguna razón para esto, ya que puedes almacenar el resultado. Esto significa que no puede usar istream_iterator.

asciihex necesita un iterador aleatorio como entrada, porque (it + 1) y (it + = 2) se usan. El algoritmo podría funcionar en un iterador directo si solo usa (++ it).

(*it > '9' ? *it - 0x07 : *it) - 0x30 se puede simplificar a *it - (*it > '9' ? 0x37 : 0x30) por lo que solo queda una resta incondicional. Aún así, una búsqueda de matriz sería más eficiente. Reste 0x30. '0' se convertirá en 0; 'A' se convertirá en 0x11 y 'a' se convertirá en 0x31. Enmascare con 0x1f para que no distinga entre mayúsculas y minúsculas, y puede hacer la búsqueda resultante en un carácter [0x20] sin riesgos de desbordamiento. Los caracteres no hexagonales solo te darán valores extraños.




Related