2012-04-07 2 views
6

sto cercando di tradurre il seguente codice Python in C++:disimballaggio carri hex-encoded

import struct 
import binascii 


inputstring = ("0000003F" "0000803F" "AD10753F" "00000080") 
num_vals = 4 

for i in range(num_vals): 
    rawhex = inputstring[i*8:(i*8)+8] 

    # <f for little endian float 
    val = struct.unpack("<f", binascii.unhexlify(rawhex))[0] 
    print val 

    # Output: 
    # 0.5 
    # 1.0 
    # 0.957285702229 
    # -0.0 

Così si legge a 32 bit nel valore della stringa esadecimale con codifica, lo trasforma in un byte-array con il metodo unhexlify e lo interpreta come valore float little-endian.

Di seguito quasi funziona, ma il codice è una specie di scadente (e l'ultimo 00000080 analizza in modo errato):

#include <sstream> 
#include <iostream> 


int main() 
{ 
    // The hex-encoded string, and number of values are loaded from a file. 
    // The num_vals might be wrong, so some basic error checking is needed. 
    std::string inputstring = "0000003F" "0000803F" "AD10753F" "00000080"; 
    int num_vals = 4; 


    std::istringstream ss(inputstring); 

    for(unsigned int i = 0; i < num_vals; ++i) 
    { 
     char rawhex[8]; 

// The ifdef is wrong. It is not the way to detect endianness (it's 
// always defined) 
#ifdef BIG_ENDIAN 
     rawhex[6] = ss.get(); 
     rawhex[7] = ss.get(); 

     rawhex[4] = ss.get(); 
     rawhex[5] = ss.get(); 

     rawhex[2] = ss.get(); 
     rawhex[3] = ss.get(); 

     rawhex[0] = ss.get(); 
     rawhex[1] = ss.get(); 
#else 
     rawhex[0] = ss.get(); 
     rawhex[1] = ss.get(); 

     rawhex[2] = ss.get(); 
     rawhex[3] = ss.get(); 

     rawhex[4] = ss.get(); 
     rawhex[5] = ss.get(); 

     rawhex[6] = ss.get(); 
     rawhex[7] = ss.get(); 
#endif 

     if(ss.good()) 
     { 
      std::stringstream convert; 
      convert << std::hex << rawhex; 
      int32_t val; 
      convert >> val; 

      std::cerr << (*(float*)(&val)) << "\n"; 
     } 
     else 
     { 
      std::ostringstream os; 
      os << "Not enough values in LUT data. Found " << i; 
      os << ". Expected " << num_vals; 
      std::cerr << os.str() << std::endl; 
      throw std::exception(); 
     } 
    } 
} 

(compila su OS X 10.7/gcc-4.2.1, con un semplice g++ blah.cpp)

In particolare, mi piacerebbe sbarazzarmi delle macro macro BIG_ENDIAN, in quanto sono sicuro che c'è un modo più bello per farlo, come discute lo this post.

Pochi altri dettagli casuali - Non riesco a utilizzare Boost (dipendenza troppo grande per il progetto). La stringa di solito contengono tra 1536 (8 * 3) e 98304 valori float (32 * 3), al massimo 786.432 (64 * 3)

(EDIT2: aggiunto un altro valore, 00000080 == -0.0)

risposta

0

Questo è quello che abbiamo finito con, OpenColorIO/src/core/FileFormatIridasLook.cpp

(risposta di Amardeep con l'unsigned uint32_t correzione sarebbe probabilmente funzionerà anche)

// convert hex ascii to int 
    // return true on success, false on failure 
    bool hexasciitoint(char& ival, char character) 
    { 
     if(character>=48 && character<=57) // [0-9] 
     { 
      ival = static_cast<char>(character-48); 
      return true; 
     } 
     else if(character>=65 && character<=70) // [A-F] 
     { 
      ival = static_cast<char>(10+character-65); 
      return true; 
     } 
     else if(character>=97 && character<=102) // [a-f] 
     { 
      ival = static_cast<char>(10+character-97); 
      return true; 
     } 

     ival = 0; 
     return false; 
    } 

    // convert array of 8 hex ascii to f32 
    // The input hexascii is required to be a little-endian representation 
    // as used in the iridas file format 
    // "AD10753F" -> 0.9572857022285461f on ALL architectures 

    bool hexasciitofloat(float& fval, const char * ascii) 
    { 
     // Convert all ASCII numbers to their numerical representations 
     char asciinums[8]; 
     for(unsigned int i=0; i<8; ++i) 
     { 
      if(!hexasciitoint(asciinums[i], ascii[i])) 
      { 
       return false; 
      } 
     } 

     unsigned char * fvalbytes = reinterpret_cast<unsigned char *>(&fval); 

#if OCIO_LITTLE_ENDIAN 
     // Since incoming values are little endian, and we're on little endian 
     // preserve the byte order 
     fvalbytes[0] = (unsigned char) (asciinums[1] | (asciinums[0] << 4)); 
     fvalbytes[1] = (unsigned char) (asciinums[3] | (asciinums[2] << 4)); 
     fvalbytes[2] = (unsigned char) (asciinums[5] | (asciinums[4] << 4)); 
     fvalbytes[3] = (unsigned char) (asciinums[7] | (asciinums[6] << 4)); 
#else 
     // Since incoming values are little endian, and we're on big endian 
     // flip the byte order 
     fvalbytes[3] = (unsigned char) (asciinums[1] | (asciinums[0] << 4)); 
     fvalbytes[2] = (unsigned char) (asciinums[3] | (asciinums[2] << 4)); 
     fvalbytes[1] = (unsigned char) (asciinums[5] | (asciinums[4] << 4)); 
     fvalbytes[0] = (unsigned char) (asciinums[7] | (asciinums[6] << 4)); 
#endif 
     return true; 
    } 
1

Penso che l'intero business istringstring sia eccessivo. È molto più semplice analizzare questa cifra una alla volta.

Innanzitutto, creare una funzione per convertire una cifra esadecimale in un numero intero:

signed char htod(char c) 
{ 
    c = tolower(c); 
    if(isdigit(c)) 
    return c - '0'; 

    if(c >= 'a' && c <= 'f') 
    return c - 'a'; 

    return -1; 
} 

Poi semplicemente convertire la stringa in un numero intero. Il codice qui sotto non controlla gli errori e assume grande endianness, ma dovresti essere in grado di inserire i dettagli.

unsigned long t = 0; 
for(int i = 0; i < s.length(); ++i) 
    t = (t << 4) & htod(s[i]); 

Poi il galleggiante è

float f = * (float *) &t; 
+0

Penso che si intende (c - 'A') + 10; supponendo che sarà solo maiuscolo A –

+0

Inoltre, il vantaggio di farlo da solo cifra per cifra è che puoi eseguire il ciclo da sinistra a destra o da destra a sinistra a seconda della endianità –

+0

@OrgnlDave - ecco perché 'tolower' c'è. Sì sulla endianità, anche se diventa leggermente più complicato (per le cifre a byte singolo non si scambiano) –

1

Di seguito è riportato il codice aggiornato modificato per rimuovere il blocco #ifdef BIG_ENDIAN. Utilizza una tecnica di lettura che dovrebbe essere indipendente dall'ordine byte host. Lo fa leggendo i byte esadecimali (che sono little endian nella stringa sorgente) in un formato di stringa big endian compatibile con l'operatore iostream std :: hex. Una volta in questo formato, non dovrebbe importare quale sia l'ordine dei byte host.

Inoltre, corregge un bug in quanto rawhex deve essere terminato da zero per essere inserito in convert senza trascinamento finale in alcuni casi.

Non ho un sistema big endian su cui eseguire il test, quindi verificare sulla propria piattaforma. Questo è stato compilato e testato sotto Cygwin.

#include <sstream> 
#include <iostream> 

int main() 
{ 
    // The hex-encoded string, and number of values are loaded from a file. 
    // The num_vals might be wrong, so some basic error checking is needed. 
    std::string inputstring = "0000003F0000803FAD10753F00000080"; 
    int num_vals = 4; 
    std::istringstream ss(inputstring); 
    size_t const k_DataSize = sizeof(float); 
    size_t const k_HexOctetLen = 2; 

    for (uint32_t i = 0; i < num_vals; ++i) 
    { 
     char rawhex[k_DataSize * k_HexOctetLen + 1]; 

     // read little endian string into memory array 
     for (uint32_t j=k_DataSize; (j > 0) && ss.good(); --j) 
     { 
      ss.read(rawhex + ((j-1) * k_HexOctetLen), k_HexOctetLen); 
     } 

     // terminate the string (needed for safe conversion) 
     rawhex[k_DataSize * k_HexOctetLen] = 0; 

     if (ss.good()) 
     { 
      std::stringstream convert; 
      convert << std::hex << rawhex; 
      uint32_t val; 
      convert >> val; 

      std::cerr << (*(float*)(&val)) << "\n"; 
     } 
     else 
     { 
      std::ostringstream os; 
      os << "Not enough values in LUT data. Found " << i; 
      os << ". Expected " << num_vals; 
      std::cerr << os.str() << std::endl; 
      throw std::exception(); 
     } 
    } 
} 
+0

Questo aspetto è molto più bello, ma alcuni valori vengono letti in modo errato rispetto al codice originale. Ho aggiornato la stringa di esempio con 'AD10753F', che dovrebbe essere circa 0.9ish, ma viene letto come 4.6e-41 o giù di lì con questo – dbr

+1

' ntohl' è sbagliato per questo uso: converte big-endian in native-endian , mentre la conversione desiderata è little-endian per native-endian. – ephemient

+0

Questa versione corregge un problema di ordine stringa nella prima versione. Tutti e tre i valori del test vengono visualizzati correttamente su una piccola macchina endian. Si prega di verificare su un sistema big endian se si ha accesso. –