将十六进制字符串转换为字节数组 (.NET) 的最佳方法是什么?

发布于 2024-07-16 09:19:51 字数 528 浏览 3 评论 0原文

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

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

但是在一般情况下呢? 是否有更好的内置函数,或者更清晰的(不一定更快,但仍然高性能)的方法来做到这一点?

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

I have a hexidecimal string that I need to convert to a byte array. The best way (ie efficient and least code) is:

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

In the case where I have a 32bit value I can do the following:

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

However what about in the general case? Is there a better built in function, or a clearer (doesn't have to be faster, but still performant) way of doing this?

I would prefer a built in function as there seems to be one for everything (well common things) except this particular conversion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

柒夜笙歌凉 2024-07-23 09:19:51

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

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

You get the best performance if you calculate the values from the character codes instead of creating substrings and parsing them.

Code in C#, that handles both upper and lower case hex (but no 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;
}

Usage:

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

As the code uses a few tricks, here a commented version:

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;
}
女皇必胜 2024-07-23 09:19:51

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

您当然可以创建一个更有效的版本,它从 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;
}

There's nothing built-in, unfortunately. (I really should have the code I've got here somewhere else - it's at least the 3rd or 4th time I've written it.)

You could certainly create a more efficient version which parsed a nybble from a char rather than taking a substring each time, but it's more code. If you're using this a lot, benchmark the original code to see whether or not it's adequate first.

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;
}
我不是你的备胎 2024-07-23 09:19:51

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

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

Take a look at this - it's very short and is part of the .NET framework:

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

时光沙漏 2024-07-23 09:19:51

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

Here's a one-liner using LINQ. It's basically just a translation of your original version:

string hexstr = "683A2134";

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

If you'll potentially need to convert strings of uneven length (ie, if they might have an implicit leading-zero) then the code becomes a bit more complicated:

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();
葮薆情 2024-07-23 09:19:51
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());
  }
}
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());
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文