Каков наилучший способ преобразования шестнадцатеричной строки в байтовый массив (.NET)? - PullRequest
2 голосов
/ 26 марта 2009

У меня есть шестнадцатеричная строка, которую мне нужно преобразовать в байтовый массив. Лучший способ (т. Е. Эффективный и наименьший код):

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

В случае, если у меня есть 32-битное значение, я могу сделать следующее:

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

Однако, что в общем случае? Есть ли лучшая встроенная функция или более понятный (не обязательно более быстрый, но все же эффективный) способ сделать это?

Я бы предпочел встроенную функцию, так как, кажется, она есть для всех (вполне распространенных вещей), кроме этого конкретного преобразования.

Ответы [ 5 ]

5 голосов
/ 26 марта 2009

Вы получите лучшую производительность, если вы вычисляете значения из кодов символов вместо создания подстрок и их анализа.

Код в C #, который обрабатывает как верхний, так и нижний регистр (без проверки):

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

Использование:

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

Поскольку в коде используется несколько хитростей, здесь прокомментированная версия:

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;
}
4 голосов
/ 26 марта 2009

Нет ничего встроенного, к сожалению. (У меня действительно должен быть код, который я получил здесь где-то еще - это как минимум 3-й или 4-й раз, когда я его написал.)

Вы, конечно, могли бы создать более эффективную версию, которая бы анализировала nybble из символа вместо того, чтобы каждый раз брать подстроку, но это больше кода. Если вы часто этим пользуетесь, сравните исходный код, чтобы убедиться в его адекватности.

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;
}
3 голосов
/ 16 апреля 2010

Взгляните на это - оно очень короткое и является частью .NET Framework:

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

0 голосов
/ 26 марта 2009

Вот одна строка, использующая LINQ. В основном это просто перевод вашей оригинальной версии:

string hexstr = "683A2134";

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

Если вам потенциально потребуется преобразовать строки неравной длины (т. Е. Если они могут иметь неявный начальный ноль), то код становится немного сложнее:

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();
0 голосов
/ 26 марта 2009
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());
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...