Existe-t-il une fonction C standard qui convertit de chaîne hexadécimale en octet tableau ?
Je ne veux pas écrire ma propre fonction.
Autant que je sache, il n'y a pas de fonction standard pour le faire, mais c'est simple à réaliser de la manière suivante:
#include <stdio.h>
int main(int argc, char **argv) {
const char hexstring[] = "DEadbeef10203040b00b1e50", *pos = hexstring;
unsigned char val[12];
/* WARNING: no sanitization or error-checking whatsoever */
for (size_t count = 0; count < sizeof val/sizeof *val; count++) {
sscanf(pos, "%2hhx", &val[count]);
pos += 2;
}
printf("0x");
for(size_t count = 0; count < sizeof val/sizeof *val; count++)
printf("%02x", val[count]);
printf("\n");
return 0;
}
Comme Al l'a souligné, dans le cas d'un nombre impair de chiffres hexadécimaux dans la chaîne, vous devez vous assurer de la préfixer avec un 0 de départ. Par exemple, la chaîne "f00f5"
sera évaluée par {0xf0, 0x0f, 0x05}
à tort par l'exemple ci-dessus, le {0x0f, 0x00, 0xf5}
approprié.
A modifié l'exemple un peu pour répondre au commentaire de @MassimoCallegari
J'ai trouvé cette question par Google pour la même chose. Je n'aime pas l'idée d'appeler sscanf () ou strtol () car cela donne l'impression d'être excessif. J'ai écrit une fonction rapide qui ne confirme pas que le texte est bien la présentation hexadécimale d'un flux d'octets, mais gérera un nombre impair de chiffres hexadécimaux:
uint8_t tallymarker_hextobin(const char * str, uint8_t * bytes, size_t blen)
{
uint8_t pos;
uint8_t idx0;
uint8_t idx1;
// mapping of ASCII characters to hex values
const uint8_t hashmap[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // !"#$%&'
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ()*+,-./
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // PQRSTUVW
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // XYZ[\]^_
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // `abcdefg
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hijklmno
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pqrstuvw
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xyz{|}~.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ........
};
bzero(bytes, blen);
for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)
{
idx0 = (uint8_t)str[pos+0];
idx1 = (uint8_t)str[pos+1];
bytes[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
};
return(0);
}
Pour les chaînes courtes, strtol
, strtoll
et strtoimax
fonctionneront parfaitement (notez que le troisième argument est la base à utiliser pour le traitement de la chaîne ... définissez-le sur 16). Si votre saisie est plus longue que number-of-bits-in-the-longest-integer-type/4
, vous aurez besoin de l’une des méthodes les plus souples suggérées par d’autres réponses.
Outre les excellentes réponses ci-dessus, j’ai pensé écrire une fonction en C qui n’utilise aucune bibliothèque et qui protège contre les mauvaises chaînes.
uint8_t* datahex(char* string) {
if(string == NULL)
return NULL;
size_t slength = strlen(string);
if((slength % 2) != 0) // must be even
return NULL;
size_t dlength = slength / 2;
uint8_t* data = malloc(dlength);
memset(data, 0, dlength);
size_t index = 0;
while (index < slength) {
char c = string[index];
int value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
else if (c >= 'a' && c <= 'f')
value = (10 + (c - 'a'));
else {
free(data);
return NULL;
}
data[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
return data;
}
Explication:
une. index/2 | La division entre les nombres entiers arrondira la valeur, donc 0/2 = 0, 1/2 = 0, 2/2 = 1, 3/2 = 0, etc. Ainsi, pour chaque chaîne de 2 caractères, nous ajoutons la valeur à 1 octet de données. .
b. (index + 1)% 2 | Nous voulons que les nombres impairs donnent 1 et même 0, car le premier chiffre d'une chaîne hexagonale est le plus significatif et doit être multiplié par 16. Ainsi, pour l'index 0 => 0 + 1% 2 = 1, l'index 1 => 1 + 1% 2 = 0 etc.
c. << 4 | Le décalage de 4 est multiplié par 16. Exemple: b00000001 << 4 = b00010000
Une version complète du post de Michael Foukarakis (puisque je n'ai pas encore la "réputation" pour ajouter un commentaire à ce post):
#include <stdio.h>
#include <string.h>
void print(unsigned char *byte_array, int byte_array_size)
{
int i = 0;
printf("0x");
for(; i < byte_array_size; i++)
{
printf("%02x", byte_array[i]);
}
printf("\n");
}
int convert(const char *hex_str, unsigned char *byte_array, int byte_array_max)
{
int hex_str_len = strlen(hex_str);
int i = 0, j = 0;
// The output array size is half the hex_str length (rounded up)
int byte_array_size = (hex_str_len+1)/2;
if (byte_array_size > byte_array_max)
{
// Too big for the output array
return -1;
}
if (hex_str_len % 2 == 1)
{
// hex_str is an odd length, so assume an implicit "0" prefix
if (sscanf(&(hex_str[0]), "%1hhx", &(byte_array[0])) != 1)
{
return -1;
}
i = j = 1;
}
for (; i < hex_str_len; i+=2, j++)
{
if (sscanf(&(hex_str[i]), "%2hhx", &(byte_array[j])) != 1)
{
return -1;
}
}
return byte_array_size;
}
void main()
{
char *examples[] = { "", "5", "D", "5D", "5Df", "deadbeef10203040b00b1e50", "02invalid55" };
unsigned char byte_array[128];
int i = 0;
for (; i < sizeof(examples)/sizeof(char *); i++)
{
int size = convert(examples[i], byte_array, 128);
if (size < 0)
{
printf("Failed to convert '%s'\n", examples[i]);
}
else if (size == 0)
{
printf("Nothing to convert for '%s'\n", examples[i]);
}
else
{
print(byte_array, size);
}
}
}
Après quelques modifications dans le code de user411313, voici ce qui fonctionne pour moi:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main ()
{
char *hexstring = "deadbeef10203040b00b1e50";
int i;
unsigned int bytearray[12];
uint8_t str_len = strlen(hexstring);
for (i = 0; i < (str_len / 2); i++) {
sscanf(hexstring + 2*i, "%02x", &bytearray[i]);
printf("bytearray %d: %02x\n", i, bytearray[i]);
}
return 0;
}
hextools.h
#ifndef HEX_TOOLS_H
#define HEX_TOOLS_H
char *bin2hex(unsigned char*, int);
unsigned char *hex2bin(const char*);
#endif // HEX_TOOLS_H
hextools.c
#include <stdlib.h>
char *bin2hex(unsigned char *p, int len)
{
char *hex = malloc(((2*len) + 1));
char *r = hex;
while(len && p)
{
(*r) = ((*p) & 0xF0) >> 4;
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
(*r) = ((*p) & 0x0F);
(*r) = ((*r) <= 9 ? '0' + (*r) : 'A' - 10 + (*r));
r++;
p++;
len--;
}
*r = '\0';
return hex;
}
unsigned char *hex2bin(const char *str)
{
int len, h;
unsigned char *result, *err, *p, c;
err = malloc(1);
*err = 0;
if (!str)
return err;
if (!*str)
return err;
len = 0;
p = (unsigned char*) str;
while (*p++)
len++;
result = malloc((len/2)+1);
h = !(len%2) * 4;
p = result;
*p = 0;
c = *str;
while(c)
{
if(('0' <= c) && (c <= '9'))
*p += (c - '0') << h;
else if(('A' <= c) && (c <= 'F'))
*p += (c - 'A' + 10) << h;
else if(('a' <= c) && (c <= 'f'))
*p += (c - 'a' + 10) << h;
else
return err;
str++;
c = *str;
if (h)
h = 0;
else
{
h = 4;
p++;
*p = 0;
}
}
return result;
}
principal c
#include <stdio.h>
#include "hextools.h"
int main(void)
{
unsigned char s[] = { 0xa0, 0xf9, 0xc3, 0xde, 0x44 };
char *hex = bin2hex(s, sizeof s);
puts(hex);
unsigned char *bin;
bin = hex2bin(hex);
puts(bin2hex(bin, 5));
size_t k;
for(k=0; k<5; k++)
printf("%02X", bin[k]);
putchar('\n');
return 0;
}
Voici la solution que j'ai écrite pour des raisons de performances:
void hex2bin(const char* in, size_t len, unsigned char* out) {
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61,
62, 63, 64, 10, 11, 12, 13, 14, 15, 71, 72, 73, 74, 75,
76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 10, 11, 12, 13, 14, 15
};
static const unsigned char *LOOKUP = TBL - 48;
const char* end = in + len;
while(in < end) *(out++) = LOOKUP[*(in++)] << 4 | LOOKUP[*(in++)];
}
Exemple:
unsigned char seckey[32];
hex2bin("351aaaec0070d13d350afae2bc43b68c7e590268889869dde489f2f7988f3fee", 64, seckey);
/*
seckey = {
53, 26, 170, 236, 0, 112, 209, 61, 53, 10, 250, 226, 188, 67, 182, 140,
126, 89, 2, 104, 136, 152, 105, 221, 228, 137, 242, 247, 152, 143, 63, 238
};
*/
Si vous n'avez pas besoin de prendre en charge les minuscules:
static const unsigned char TBL[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59,
60, 61, 62, 63, 64, 10, 11, 12, 13, 14, 15
};
Non, mais il est relativement facile d’atteindre sscanf
dans une boucle.
char *hexstring = "deadbeef10203040b00b1e50", *pos = hexstring;
unsigned char val[12];
while( *pos )
{
if( !((pos-hexstring)&1) )
sscanf(pos,"%02x",&val[(pos-hexstring)>>1]);
++pos;
}
sizeof (val)/sizeof (val [0]) est redondant!
In main()
{
printf("enter string :\n");
fgets(buf, 200, stdin);
unsigned char str_len = strlen(buf);
k=0;
unsigned char bytearray[100];
for(j=0;j<str_len-1;j=j+2)
{ bytearray[k++]=converttohex(&buffer[j]);
printf(" %02X",bytearray[k-1]);
}
}
Use this
int converttohex(char * val)
{
unsigned char temp = toupper(*val);
unsigned char fin=0;
if(temp>64)
temp=10+(temp-65);
else
temp=temp-48;
fin=(temp<<4)&0xf0;
temp = toupper(*(val+1));
if(temp>64)
temp=10+(temp-65);
else
temp=temp-48;
fin=fin|(temp & 0x0f);
return fin;
}
Voici que HexToBin et BinToHex sont relativement propres et lisibles .
typedef unsigned char ByteData;
ByteData HexChar (char c)
{
if ('0' <= c && c <= '9') return (ByteData)(c - '0');
if ('A' <= c && c <= 'F') return (ByteData)(c - 'A' + 10);
if ('a' <= c && c <= 'f') return (ByteData)(c - 'a' + 10);
return (ByteData)(-1);
}
ssize_t HexToBin (const char* s, ByteData * buff, ssize_t length)
{
ssize_t result = 0;
if (!s || !buff || length <= 0) return -2;
while (*s)
{
ByteData nib1 = HexChar(*s++);
if ((signed)nib1 < 0) return -3;
ByteData nib2 = HexChar(*s++);
if ((signed)nib2 < 0) return -4;
ByteData bin = (nib1 << 4) + nib2;
if (length-- <= 0) return -5;
*buff++ = bin;
++result;
}
return result;
}
void BinToHex (const ByteData * buff, ssize_t length, char * output, ssize_t outLength)
{
char binHex[] = "0123456789ABCDEF";
if (!output || outLength < 4) return (void)(-6);
*output = '\0';
if (!buff || length <= 0 || outLength <= 2 * length)
{
memcpy(output, "ERR", 4);
return (void)(-7);
}
for (; length > 0; --length, outLength -= 2)
{
ByteData byte = *buff++;
*output++ = binHex[(byte >> 4) & 0x0F];
*output++ = binHex[byte & 0x0F];
}
if (outLength-- <= 0) return (void)(-8);
*output++ = '\0';
}
Ceci est une fonction modifiée à partir d'une question similaire, modifiée selon la suggestion de https://stackoverflow.com/a/18267932/700597 .
Cette fonction convertira une chaîne hexadécimale - PAS précédée de "0x" - avec un nombre pair de caractères pour le nombre d'octets spécifié. Il renverra -1 s'il rencontre un caractère non valide ou si la chaîne hexagonale a une longueur impaire, et 0 en cas de succès.
//convert hexstring to len bytes of data
//returns 0 on success, -1 on error
//data is a buffer of at least len bytes
//hexstring is upper or lower case hexadecimal, NOT prepended with "0x"
int hex2data(unsigned char *data, const unsigned char *hexstring, unsigned int len)
{
unsigned const char *pos = hexstring;
char *endptr;
size_t count = 0;
if ((hexstring[0] == '\0') || (strlen(hexstring) % 2)) {
//hexstring contains no data
//or hexstring has an odd length
return -1;
}
for(count = 0; count < len; count++) {
char buf[5] = {'0', 'x', pos[0], pos[1], 0};
data[count] = strtol(buf, &endptr, 0);
pos += 2 * sizeof(char);
if (endptr[0] != '\0') {
//non-hexadecimal character encountered
return -1;
}
}
return 0;
}
Voici ma version:
/* Convert a hex char digit to its integer value. */
int hexDigitToInt(char digit) {
digit = tolower(digit);
if ('0' <= digit && digit <= '9') //if it's decimal
return (int)(digit - '0');
else if ('a' <= digit && digit <= 'f') //if it's abcdef
return (int)(digit - ('a' - 10));
else
return -1; //value not in [0-9][a-f] range
}
/* Decode a hex string. */
char *decodeHexString(const char *hexStr) {
char* decoded = malloc(strlen(hexStr)/2+1);
char* hexStrPtr = (char *)hexStr;
char* decodedPtr = decoded;
while (*hexStrPtr != '\0') { /* Step through hexStr, two chars at a time. */
*decodedPtr = 16 * hexDigitToInt(*hexStrPtr) + hexDigitToInt(*(hexStrPtr+1));
hexStrPtr += 2;
decodedPtr++;
}
*decodedPtr = '\0'; /* final null char */
return decoded;
}
La meilleure façon que je connaisse:
int hex2bin_by_zibri(char *source_str, char *dest_buffer)
{
char *line = source_str;
char *data = line;
int offset;
int read_byte;
int data_len = 0;
while (sscanf(data, " %02x%n", &read_byte, &offset) == 1) {
dest_buffer[data_len++] = read_byte;
data += offset;
}
return data_len;
}
La fonction renvoie le nombre d'octets convertis enregistrés dans dest_buffer. La chaîne d'entrée peut contenir des espaces et des lettres en majuscules.
"01 02 03 04 ab Cd eF ordures AB"
traduit en dest_buffer contenant 01 02 03 04 ab cd ef
et aussi "01020304abCdeFgarbageAB"
traduit comme avant.
L'analyse s'arrête à la première "erreur".
Pourrait-il être plus simple?!
uint8_t hex(char ch) {
uint8_t r = (ch > 57) ? (ch - 55) : (ch - 48);
return r & 0x0F;
}
int to_byte_array(const char *in, size_t in_size, uint8_t *out) {
int count = 0;
if (in_size % 2) {
while (*in && out) {
*out = hex(*in++);
if (!*in)
return count;
*out = (*out << 4) | hex(*in++);
*out++;
count++;
}
return count;
} else {
while (*in && out) {
*out++ = (hex(*in++) << 4) | hex(*in++);
count++;
}
return count;
}
}
int main() {
char hex_in[] = "deadbeef10203040b00b1e50";
uint8_t out[32];
int res = to_byte_array(hex_in, sizeof(hex_in) - 1, out);
for (size_t i = 0; i < res; i++)
printf("%02x ", out[i]);
printf("\n");
system("pause");
return 0;
}
Essayez le code suivant:
static unsigned char ascii2byte(char *val)
{
unsigned char temp = *val;
if(temp > 0x60) temp -= 39; // convert chars a-f
temp -= 48; // convert chars 0-9
temp *= 16;
temp += *(val+1);
if(*(val+1) > 0x60) temp -= 39; // convert chars a-f
temp -= 48; // convert chars 0-9
return temp;
}