如何将字节数组转换为十六进制字符串,反之亦然?


当前回答

用@CodesInChaus补充答案(反向方法)

public static byte[] HexToByteUsingByteManipulation(string s)
{
    byte[] bytes = new byte[s.Length / 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        int hi = s[i*2] - 65;
        hi = hi + 10 + ((hi >> 31) & 7);

        int lo = s[i*2 + 1] - 65;
        lo = lo + 10 + ((lo >> 31) & 7) & 0x0f;

        bytes[i] = (byte) (lo | hi << 4);
    }
    return bytes;
}

说明:

&0x0f还支持小写字母

hi=hi+10+((hi>>31)&7);与以下内容相同:

hi=ch-65+10+((ch-65)>>31)&7);

对于“0”9’与hi=ch-65+10+7相同;其为hi=ch-48(这是因为0xffffff&7)。

对于“A”F’为hi=ch-65+10;(这是因为0x00000000&7)。

对于“a”我们必须使用大数字,所以我们必须通过使用&0x0f使某些位为0,从默认版本中减去32。

65是“A”的代码

48是“0”的代码

7是ASCII表中“9”和“A”之间的字母数(…456789:;<=>?@ABCD…)。

其他回答

这里不想赘述很多答案,但我发现了一个十六进制字符串解析器的相当优化(比公认的好4.5倍)、简单的实现。首先,我的测试输出(第一批是我的实现):

Give me that string:
04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939f

Time to parse 100,000 times: 50.4192 ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

Accepted answer: (StringToByteArray)
Time to parse 100000 times: 233.1264ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With Mono's implementation:
Time to parse 100000 times: 777.2544ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With SoapHexBinary:
Time to parse 100000 times: 845.1456ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

base64和“BitConverter'd”行用于测试正确性。请注意,它们是相等的。

实施:

public static byte[] ToByteArrayFromHex(string hexString)
{
  if (hexString.Length % 2 != 0) throw new ArgumentException("String must have an even length");
  var array = new byte[hexString.Length / 2];
  for (int i = 0; i < hexString.Length; i += 2)
  {
    array[i/2] = ByteFromTwoChars(hexString[i], hexString[i + 1]);
  }
  return array;
}

private static byte ByteFromTwoChars(char p, char p_2)
{
  byte ret;
  if (p <= '9' && p >= '0')
  {
    ret = (byte) ((p - '0') << 4);
  }
  else if (p <= 'f' && p >= 'a')
  {
    ret = (byte) ((p - 'a' + 10) << 4);
  }
  else if (p <= 'F' && p >= 'A')
  {
    ret = (byte) ((p - 'A' + 10) << 4);
  } else throw new ArgumentException("Char is not a hex digit: " + p,"p");

  if (p_2 <= '9' && p_2 >= '0')
  {
    ret |= (byte) ((p_2 - '0'));
  }
  else if (p_2 <= 'f' && p_2 >= 'a')
  {
    ret |= (byte) ((p_2 - 'a' + 10));
  }
  else if (p_2 <= 'F' && p_2 >= 'A')
  {
    ret |= (byte) ((p_2 - 'A' + 10));
  } else throw new ArgumentException("Char is not a hex digit: " + p_2, "p_2");

  return ret;
}

我尝试了一些不安全的东西,并将(显然是冗余的)字符移动到另一个方法来蚕食if序列,但这是最快的。

(我承认这回答了一半的问题。我觉得字符串->字节[]转换不足,而字节[]->字符串角度似乎被很好地覆盖了。因此,这个答案。)

扩展BigInteger方法(Gregory Morse在上面提到过)。我不能评论效率,它使用System.Linq.Reverse(),但它很小而且内置。

        // To hex
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes("Test String!£");
        string hexString = new System.Numerics.BigInteger(bytes.Reverse().ToArray()).ToString("x2");

        // From hex
        byte[] fromHexBytes = System.Numerics.BigInteger.Parse(hexString, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray();

        // Unit test
        CollectionAssert.AreEqual(bytes, fromHexBytes);

支持最短路径和.net核心:

    public static string BytesToString(byte[] ba) =>
        ba.Aggregate(new StringBuilder(32), (sb, b) => sb.Append(b.ToString("X2"))).ToString();

有一个简单的一行解决方案尚未提及,它将十六进制字符串转换为字节数组(我们不在乎这里的否定解释,因为这无关紧要):

BigInteger.Parse(str, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray();

扩展方法(免责声明:完全未经测试的代码,BTW…):

public static class ByteExtensions
{
    public static string ToHexString(this byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);

        foreach (byte b in ba)
        {
            hex.AppendFormat("{0:x2}", b);
        }
        return hex.ToString();
    }
}

使用Tomalak的三种解决方案之一(最后一种是字符串上的扩展方法)。