Come trasformare una stringa esadecimale in un array di car unsigned?

Ad esempio, ho un cstring "E8 48 D8 FF FF 8B 0D" (spazi inclusi) che deve essere convertito nell’equivalente char array unsigned {0xE8,0x48,0xD8,0xFF,0xFF,0x8B,0x0D} . Qual è un modo efficace per farlo? Grazie!

EDIT: non riesco a usare la libreria std … quindi considera una domanda in C. Mi dispiace!

Non mi convincerai mai che questa operazione è un collo di bottiglia per le prestazioni. Il modo più efficace consiste nel fare buon uso del tempo utilizzando la libreria C standard:

 static unsigned char gethex(const char *s, char **endptr) { assert(s); while (isspace(*s)) s++; assert(*s); return strtoul(s, endptr, 16); } unsigned char *convert(const char *s, int *length) { unsigned char *answer = malloc((strlen(s) + 1) / 3); unsigned char *p; for (p = answer; *s; p++) *p = gethex(s, (char **)&s); *length = p - answer; return answer; } 

Compilato e testato Funziona sul tuo esempio.

Questo risponde alla domanda originale , che chiedeva una soluzione C ++.

Puoi usare un istringstream con il manipolatore hex :

 std::string hex_chars("E8 48 D8 FF FF 8B 0D"); std::istringstream hex_chars_stream(hex_chars); std::vector bytes; unsigned int c; while (hex_chars_stream >> std::hex >> c) { bytes.push_back(c); } 

Nota che c deve essere un int (o long o qualche altro tipo intero), non un char ; se è un char (o un char unsigned char ), verrà chiamato il sovraccarico >> sbagliato e verranno estratti singoli caratteri dalla stringa, non stringhe di numeri esadecimali.

Ulteriore verifica degli errori per garantire che il valore estratto si adatti a un char sarebbe una buona idea.

  • Iterate attraverso tutti i personaggi.
    • Se hai una cifra esadecimale, il numero è (ch >= 'A')? (ch - 'A' + 10): (ch - '0') (ch >= 'A')? (ch - 'A' + 10): (ch - '0') .
      • Sinistra sposta l’accumulatore di quattro bit e aggiungi (o OR) nella nuova cifra.
    • Se si dispone di uno spazio e il carattere precedente non era uno spazio, aggiungere il valore dell’accumulatore corrente all’array e ripristinare l’accumulatore a zero.

Se conosci la lunghezza della stringa da analizzare in anticipo (ad esempio stai leggendo qualcosa da / proc) puoi usare sscanf con il modificatore di tipo ‘hh’, che specifica che la prossima conversione è uno di diox e il puntatore per memorizzarlo sarà firmato char o unsigned char.

 // example: ipv6 address as seen in /proc/net/if_inet6: char myString[] = "fe80000000000000020c29fffe01bafb"; unsigned char addressBytes[16]; sscanf(myString, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", &addressBytes[0], &addressBytes[1], &addressBytes[2], &addressBytes[3], &addressBytes[4], &addressBytes[5], &addressBytes[6], &addressBytes[7], &addressBytes[8], &addressBytes[9], &addressBytes[10], addressBytes[11],&addressBytes[12], &addressBytes[13], &addressBytes[14], &addressBytes[15]); int i; for (i = 0; i < 16; i++){ printf("addressBytes[%d] = %02x\n", i, addressBytes[i]); } 

Produzione:

 addressBytes[0] = fe addressBytes[1] = 80 addressBytes[2] = 00 addressBytes[3] = 00 addressBytes[4] = 00 addressBytes[5] = 00 addressBytes[6] = 00 addressBytes[7] = 00 addressBytes[8] = 02 addressBytes[9] = 0c addressBytes[10] = 29 addressBytes[11] = ff addressBytes[12] = fe addressBytes[13] = 01 addressBytes[14] = ba addressBytes[15] = fb 

usa la “vecchia” funzione sscanf ():

 string s_hex = "E8 48 D8 FF FF 8B 0D"; // source string char *a_Char = new char( s_hex.length()/3 +1 ); // output char array for( unsigned i = 0, uchr ; i < s_hex.length() ; i += 3 ) { sscanf( s_hex.c_str()+ i, "%2x", &uchr ); // conversion a_Char[i/3] = uchr; // save as char } delete a_Char; 

Per un’implementazione pura di C penso che tu possa convincere sscanf(3) a fare ciò che tu. Credo che questo dovrebbe essere portatile (inclusa la coercizione di tipo un po ‘dubbia per placare il compilatore) fintanto che la stringa di input conterrà solo valori esadecimali di due caratteri.

 #include  #include  char hex[] = "E8 48 D8 FF FF 8B 0D"; char *p; int cnt = (strlen(hex) + 1) / 3; // Whether or not there's a trailing space unsigned char *result = (unsigned char *)malloc(cnt), *r; unsigned char c; for (p = hex, r = result; *p; p += 3) { if (sscanf(p, "%02X", (unsigned int *)&c) != 1) { break; // Didn't parse as expected } *r++ = c; } 

Il vecchio modo C, fallo a mano 😉 (ci sono molti modi più corti, ma non sto giocando a golf, vado per il tempo di esecuzione).

 enum { NBBYTES = 7 }; char res[NBBYTES+1]; const char * c = "E8 48 D8 FF FF 8B 0D"; const char * p = c; int i = 0; for (i = 0; i < NBBYTES; i++){ switch (*p){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': res[i] = *p - '0'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': res[i] = *p - 'A' + 10; break; default: // parse error, throw exception ; } p++; switch (*p){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': res[i] = res[i]*16 + *p - '0'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': res[i] = res[i]*16 + *p - 'A' + 10; break; default: // parse error, throw exception ; } p++; if (*p == 0) { continue; } if (*p == ' ') { p++; continue; } // parse error, throw exception } // let's show the result, C style IO, just cout if you want C++ for (i = 0 ; i < 7; i++){ printf("%2.2x ", 0xFF & res[i]); } printf("\n"); 

Ora un altro che consente qualsiasi numero di cifre tra i numeri, qualsiasi numero di spazi per separarli, inclusi gli spazi iniziali o finali (le specifiche di Ben):

 #include  #include  int main(){ enum { NBBYTES = 7 }; char res[NBBYTES]; const char * c = "E8 48 D8 FF FF 8B 0D"; const char * p = c; int i = -1; res[i] = 0; char ch = ' '; while (ch && i < NBBYTES){ switch (ch){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ch -= '0' + 10 - 'A'; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': ch -= 'A' - 10; res[i] = res[i]*16 + ch; break; case ' ': if (*p != ' ') { if (i == NBBYTES-1){ printf("parse error, throw exception\n"); exit(-1); } res[++i] = 0; } break; case 0: break; default: printf("parse error, throw exception\n"); exit(-1); } ch = *(p++); } if (i != NBBYTES-1){ printf("parse error, throw exception\n"); exit(-1); } for (i = 0 ; i < 7; i++){ printf("%2.2x ", 0xFF & res[i]); } printf("\n"); } 

No, non è davvero offuscato ... ma beh, sembra proprio che lo sia.