2009-03-26 8 views
2

J'ai une chaîne hexadécimale que j'ai besoin de convertir en un tableau d'octets. La meilleure façon (c.-à-efficace et moins code) est:Quelle est la meilleure façon de convertir une chaîne hexadécimale en un tableau d'octets (.NET)?

string hexstr = "683A2134"; 
byte[] bytes = new byte[hexstr.Length/2]; 
for(int x = 0; x < bytes.Length; x++) 
{ 
    bytes[x] = Convert.ToByte(hexstr.Substring(x * 2, 2), 16); 
} 

Dans le cas où j'ai une valeur de 32 bits, je peux faire ce qui suit:

string hexstr = "683A2134"; 
byte[] bytes = BitConverter.GetBytes(Convert.ToInt32(hexstr, 16)); 

Cependant qu'en est dans le cas général? Y a-t-il une meilleure fonction intégrée, ou une façon plus claire (qui ne doit pas être plus rapide, mais toujours performante) de le faire?

Je préférerais une fonction intégrée car il semble y en avoir une pour tout (bien des choses communes) sauf cette conversion particulière.

+0

Notez que votre solution et acceptée la solution échouera si elle passe une chaîne de longueur impaire. Dans le cas de "A", par exemple, le tableau d'octets retourné n'aura rien dedans. –

+0

@Jim, je viens de poster une réponse à cette question, bien qu'il soit assez facile de corriger les autres solutions. – LukeH

+0

Je le réalise. Je supposais une chaîne validée. –

Répondre

5

Vous obtenez les meilleures performances si vous calculez les valeurs des codes de caractères au lieu de créer des sous-chaînes et les analyse.

code en C#, qui gère les deux hex majuscules et minuscules (mais pas de validation):

static byte[] ParseHexString(string hex) { 
    byte[] bytes = new byte[hex.Length/2]; 
    int shift = 4; 
    int offset = 0; 
    foreach (char c in hex) { 
     int b = (c - '0') % 32; 
     if (b > 9) b -= 7; 
     bytes[offset] |= (byte)(b << shift); 
     shift ^= 4; 
     if (shift != 0) offset++; 
    } 
    return bytes; 
} 

Utilisation:

byte[] bytes = ParseHexString("1fAB44AbcDEf00"); 

Comme le code utilise quelques astuces, voici une version commentée :

static byte[] ParseHexString(string hex) { 
    // array to put the result in 
    byte[] bytes = new byte[hex.Length/2]; 
    // variable to determine shift of high/low nibble 
    int shift = 4; 
    // offset of the current byte in the array 
    int offset = 0; 
    // loop the characters in the string 
    foreach (char c in hex) { 
     // get character code in range 0-9, 17-22 
     // the % 32 handles lower case characters 
     int b = (c - '0') % 32; 
     // correction for a-f 
     if (b > 9) b -= 7; 
     // store nibble (4 bits) in byte array 
     bytes[offset] |= (byte)(b << shift); 
     // toggle the shift variable between 0 and 4 
     shift ^= 4; 
     // move to next byte 
     if (shift != 0) offset++; 
    } 
    return bytes; 
} 
+0

Difficile de choisir entre celui-ci et Johns. Johns est beaucoup plus facile à comprendre, mais je pense que celui-ci est plus «élégant» –

4

Malheureusement, rien n'est intégré. (Je devrais vraiment avoir le code que j'ai ici ailleurs - c'est au moins la 3ème ou la 4ème fois que je l'ai écrit.)

Vous pourriez certainement créer une version plus efficace qui analyse un nybble d'un char plutôt que de prendre une sous-chaîne à chaque fois, mais c'est plus de code. Si vous l'utilisez beaucoup, comparez le code original pour voir s'il est adéquat en premier.

private static int ParseNybble(char nybble) 
{ 
    // Alternative implementations: use a lookup array 
    // after doing some bounds checking, or use 
    // if (nybble >= '0' && nybble <= '9') return nybble-'0' etc 
    switch (nybble) 
    { 
     case '0' : return 0; 
     case '1' : return 1; 
     case '2' : return 2; 
     case '3' : return 3; 
     case '4' : return 4; 
     case '5' : return 5; 
     case '6' : return 6; 
     case '7' : return 7; 
     case '8' : return 8; 
     case '9' : return 9; 
     case 'a': case 'A' : return 10; 
     case 'b': case 'B' : return 11; 
     case 'c': case 'C' : return 12; 
     case 'd': case 'D' : return 13; 
     case 'e': case 'E' : return 14; 
     case 'f': case 'F' : return 15; 
     default: throw new ArgumentOutOfRangeException(); 
    } 
} 

public static byte[] ParseHex(string hex) 
{ 
    // Do error checking here - hex is null or odd length 
    byte[] ret = new byte[hex.Length/2]; 
    for (int i=0; i < ret.Length; i++) 
    { 
     ret[i] = (byte) ((ParseNybble(hex[i*2]) << 4) | 
         (ParseNybble(hex[i*2+1]))); 
    } 
    return ret; 
} 
+0

Merci d'avoir repéré l'erreur (je jouais avec 2 versions différentes) –

0
public class HexCodec { 
    private static final char[] kDigits = 
     { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
     'a', 'b', 'c', 'd', 'e', 'f' }; 

    public static byte[] HexToBytes(char[] hex) { 
    int length = hex.length/2; 
    byte[] raw = new byte[length]; 
    for (int i = 0; i < length; i++) { 
     int high = Character.digit(hex[i * 2], 16); 
     int low = Character.digit(hex[i * 2 + 1], 16); 
     int value = (high << 4) | low; 
     if (value > 127) 
     value -= 256; 
     raw[i] = (byte) value; 
    } 
    return raw; 
    } 

    public static byte[] HexToBytes(String hex) { 
    return hexToBytes(hex.toCharArray()); 
    } 
} 
+0

N'est-ce pas la même chose que mon exemple? –

+0

Non - il avale les exceptions et renvoie de mauvaises données;) –

+0

C'est vrai. Je travaille avec des données validées, je sais que c'est une chaîne hexadécimale valide. –

0

Voici un one-liner en utilisant LINQ. Il est fondamentalement juste une traduction de votre version originale:

string hexstr = "683A2134"; 

byte[] bytes = Enumerable.Range(0, hexstr.Length/2) 
    .Select((x, i) => Convert.ToByte(hexstr.Substring(i * 2, 2), 16)) 
    .ToArray(); 

Si vous aurez besoin potentiellement pour convertir des chaînes de longueur inégale (par exemple, si elles peuvent avoir un avant-zéro implicite), le code devient un peu plus compliqué :

string hexstr = "683A2134F"; // should be treated as "0683A2134F" 

byte[] bytes = Enumerable.Range(0, (hexstr.Length/2) + (hexstr.Length & 1)) 
    .Select((x, i) => Convert.ToByte(hexstr.Substring((i * 2) - (i == 0 ? 0 : hexstr.Length & 1), 2 - (i == 0 ? hexstr.Length & 1 : 0)), 16)) 
    .ToArray(); 
3

Jetez un oeil à ce - il est très court et fait partie du framework .NET:

System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary.Parse("C3B01051359947").Value

+0

Approuvé. Il fonctionne comme un charme. Merci. –

Questions connexes