【问题标题】:What is the best way to convert a hexidecimal string to a byte array (.NET)?将十六进制字符串转换为字节数组(.NET)的最佳方法是什么?
【发布时间】:2009-03-26 08:21:45
【问题描述】:

我有一个需要转换为字节数组的十六进制字符串。最好的方式(即高效且代码最少)是:

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

但是在一般情况下呢?有没有更好的内置功能,或者更清晰(不一定要更快,但仍然高效)的方式来做到这一点?

我更喜欢一个内置函数,因为除了这个特定的转换之外,似乎所有东西(很常见的东西)都有一个。

【问题讨论】:

  • 请注意,如果传递一个奇数长度的字符串,您的解决方案和接受的解决方案将失败。例如,在“A”的情况下,返回的字节数组中将没有任何内容。
  • @Jim,我刚刚发布了一个解决该问题的答案,尽管修复其他解决方案也很容易。
  • 我意识到这一点。我假设一个经过验证的字符串。

标签: .net hex


【解决方案1】:

如果您从字符代码计算值而不是创建子字符串并解析它们,您将获得最佳性能。

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

【讨论】:

  • 很难在这个和约翰之间做出决定。约翰斯更容易理解,但我认为这个更“优雅”
【解决方案2】:

不幸的是,没有任何内置功能。 (我真的应该有我在其他地方得到的代码 - 这至少是我第三次或第四次编写它。)

您当然可以创建一个更高效的版本,从 char 中解析 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;
}

【讨论】:

  • 感谢您发现错误(我在玩 2 个不同的版本)
【解决方案3】:

看看这个 - 它很短,是 .NET 框架的一部分:

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

【讨论】:

    【解决方案4】:

    这是使用 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();
    

    【讨论】:

      【解决方案5】:
      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());
        }
      }
      

      【讨论】:

      • 这不是和我的例子一样吗?
      • 不 - 它吞下异常并返回错误数据;)
      • 确实如此。我正在使用经过验证的数据,但我知道这是一个有效的十六进制字符串。
      • 这不是 C#,那是什么? J#?
      • @Robert:“final”关键字、不区分大小写的标识符、“Character”类...
      猜你喜欢
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 2021-10-31
      • 2019-02-12
      相关资源
      最近更新 更多