68 votes

Chaîne hexadécimale en tableau d'octets en C

Existe-t-il une fonction C standard qui convertit de chaîne hexadécimale en tableau d'octets ?
Je ne veux pas écrire ma propre fonction.

1 votes

Voulez-vous dire une chaîne de caractères, qui contient des caractères représentant un nombre hexadécimal ?

1 votes

Oui, j'ai une chaîne de caractères entrée par l'utilisateur telle que "abCD12ff34" avec n'importe quelle longueur >= 0 et je veux la convertir en un tableau d'octets comme 0xaa, 0xcd, 0x12 etc

79voto

Michael Foukarakis Points 14892

Pour 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;
}

Editar

Comme l'a souligné Al, si le nombre de chiffres hexadécimaux dans la chaîne est impair, il faut veiller à la préfixer avec un 0 initial. Par exemple, la chaîne "f00f5" sera évalué comme {0xf0, 0x0f, 0x05} erronée par l'exemple ci-dessus, au lieu de la bonne {0x0f, 0x00, 0xf5} .

Modification de l'exemple pour répondre au commentaire de @MassimoCallegari.

6 votes

C'est une bonne approche, mais sachez qu'elle donnera un résultat incorrect si la chaîne hexagonale comporte un nombre impair de chiffres (le zéro implicite préfixera le dernier chiffre, et non le premier, de sorte que "5ab5c" sera imprimé sous la forme 0x5ab50c plutôt que 0x05ab5c).

0 votes

L'endiannité n'a-t-elle pas d'importance ici ?

1 votes

@Geremia non. L'endiveté n'entre en jeu que lorsque vous réinterprétez des tableaux d'octets comme des types entiers ou vice-versa. commandcenter.blogspot.com/2012/04/byte-order-fallacy.html ED : sauf si vous êtes bizarre et que vous écrivez votre littéral hexagonal dans l'ordre little-endian. Dans tous les cas, cela prend chaque paire de chiffres et les transforme en un seul octet ; l'endianness résultant est le même que l'original.

21voto

David M. Syzdek Points 4297

J'ai trouvé cette question en cherchant la même chose sur Google. Je n'aime pas l'idée d'appeler sscanf() ou strtol() car cela me semble excessif. J'ai écrit une fonction rapide qui ne valide pas que le texte est bien la présentation hexadécimale d'un flux d'octets, mais qui gère les nombres impairs 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);
}

7 votes

Vous pouvez réduire la taille de la carte à 32 octets en ajoutant simplement 4 instructions logiques, ce qui est un bon compromis : idx0 = (uint8_t)str[pos+0] & 0x1F ^ 0x10; et de même pour idx1 . alors vous pouvez supprimer tous les octets situés avant l'élément 01234567 et tous les octets après la ligne HIJKLMNO rangée

0 votes

@5andr0, Posté comme un résumé : gist.github.com/vi/dd3b5569af8a26b97c8e20ae06e804cb . Non vérifié bien que.

2 votes

sscanf y strtol sont superflus mais une table hexagonale inutile de 32 lignes ne l'est pas ?

9voto

dmckee Points 50318

Pour les cordes courtes, strtol , strtoll et strtoimax fonctionnera très bien (notez que le troisième argument est la base à utiliser pour traiter la chaîne de caractères... mettez-la à 16). Si votre entrée est plus longue que number-of-bits-in-the-longest-integer-type/4 alors vous aurez besoin de l'une des méthodes plus souples suggérées par les autres réponses.

2voto

Cela ne fonctionne pas pour moi. Dans mon cas, sscanf écrit 4 octets à chaque itération. J'exécute ce code sur Windows 7 32-bit, Visual C++ 2005. Je dois convertir une chaîne hexa de 8 caractères ("01020000"). Mon tampon val a donc une longueur de 4 octets.

Quand je regarde le vidage de la mémoire, j'ai obtenu ceci avant d'exécuter le code :

cd cd cd cd fd fd fd fd ab ab ab ab ab ab ab ab

A la première itération, les 4 octets sont modifiés en même temps :

01 00 00 00 fd fd fd fd ab ab ab ab ab ab ab ab

A la deuxième itération :

01 02 00 00 00 fd fd fd ab ab ab ab ab ab ab ab

Il y a ici une écriture hors limite. Je corrige cela avec une variable courte intermédiaire, et un seul 'h' dans le format sscanf.

short tmpShort;
for(count = 0; count < sizeof(val)/sizeof(val[0]); count++) {
    sscanf(pos, "%2hx", &tmpShort);
    val[count] = tmpShort;
    pos += 2 * sizeof(char);
}

Quelqu'un est-il confronté au même problème ?

-1voto

Oli Charlesworth Points 148744

Non. Mais c'est relativement trivial à réaliser en utilisant sscanf dans une boucle.

1 votes

Aucune question posée sur SO ne devrait avoir "trivial" dans la réponse, sinon la question n'aurait pas de raison d'exister

0 votes

Et oliver ne le sait manifestement pas, sinon il aurait posté la boucle "triviale" dans la réponse :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X