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


当前回答

static string ByteArrayToHexViaLookupPerByte2(byte[] bytes)
{                
        var result3 = new uint[bytes.Length];
        for (int i = 0; i < bytes.Length; i++)
                result3[i] = _Lookup32[bytes[i]];
        var handle = GCHandle.Alloc(result3, GCHandleType.Pinned);
        try
        {
                var result = Marshal.PtrToStringUni(handle.AddrOfPinnedObject(), bytes.Length * 2);
                return result;
        }
        finally
        {
                handle.Free();
        }
}

在我的测试中,这个函数总是不安全实现之后的第二个条目。

不幸的是,测试台不太可靠。。。如果你多次运行它,列表会被打乱,以至于谁知道在不安全之后哪个才是最快的!它没有考虑预热、jit编译时间和GC性能影响。我很想重写它以获得更多信息,但我真的没有时间。

其他回答

多样性的另一种变化:

public static byte[] FromHexString(string src)
{
    if (String.IsNullOrEmpty(src))
        return null;

    int index = src.Length;
    int sz = index / 2;
    if (sz <= 0)
        return null;

    byte[] rc = new byte[sz];

    while (--sz >= 0)
    {
        char lo = src[--index];
        char hi = src[--index];

        rc[sz] = (byte)(
            (
                (hi >= '0' && hi <= '9') ? hi - '0' :
                (hi >= 'a' && hi <= 'f') ? hi - 'a' + 10 :
                (hi >= 'A' && hi <= 'F') ? hi - 'A' + 10 :
                0
            )
            << 4 | 
            (
                (lo >= '0' && lo <= '9') ? lo - '0' :
                (lo >= 'a' && lo <= 'f') ? lo - 'a' + 10 :
                (lo >= 'A' && lo <= 'F') ? lo - 'A' + 10 :
                0
            )
        );
    }

    return rc;          
}

就速度而言,这似乎比这里的任何东西都要好:

  public static string ToHexString(byte[] data) {
    byte b;
    int i, j, k;
    int l = data.Length;
    char[] r = new char[l * 2];
    for (i = 0, j = 0; i < l; ++i) {
      b = data[i];
      k = b >> 4;
      r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30);
      k = b & 15;
      r[j++] = (char)(k > 9 ? k + 0x37 : k + 0x30);
    }
    return new string(r);
  }

为了方便以后复制和粘贴,将几个答案合并到一个类中:

/// <summary>
/// Extension methods to quickly convert byte array to string and back.
/// </summary>
public static class HexConverter
{
    /// <summary>
    /// Map values to hex digits
    /// </summary>
    private static readonly char[] HexDigits =
        {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };

    /// <summary>
    /// Map 56 characters between ['0', 'F'] to their hex equivalents, and set invalid characters
    /// such that they will overflow byte to fail conversion.
    /// </summary>
    private static readonly ushort[] HexValues =
        {
            0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
            0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
            0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x000A, 0x000B,
            0x000C, 0x000D, 0x000E, 0x000F
        };

    /// <summary>
    /// Empty byte array 
    /// </summary>
    private static readonly byte[] Empty = new byte[0];

    /// <summary>
    /// Convert a byte array to a hexadecimal string.
    /// </summary>
    /// <param name="bytes">
    /// The input byte array.
    /// </param>
    /// <returns>
    /// A string of hexadecimal digits.
    /// </returns>
    public static string ToHexString(this byte[] bytes)
    {
        var c = new char[bytes.Length * 2];
        for (int i = 0, j = 0; i < bytes.Length; i++)
        {
            c[j++] = HexDigits[bytes[i] >> 4];
            c[j++] = HexDigits[bytes[i] & 0x0F];
        }

        return new string(c);
    }

    /// <summary>
    /// Parse a string of hexadecimal digits into a byte array.
    /// </summary>
    /// <param name="hexadecimalString">
    /// The hexadecimal string.
    /// </param>
    /// <returns>
    /// The parsed <see cref="byte[]"/> array.
    /// </returns>
    /// <exception cref="ArgumentException">
    /// The input string either contained invalid characters, or was of an odd length.
    /// </exception>
    public static byte[] ToByteArray(string hexadecimalString)
    {
        if (!TryParse(hexadecimalString, out var value))
        {
            throw new ArgumentException("Invalid hexadecimal string", nameof(hexadecimalString));
        }

        return value;
    }

    /// <summary>
    /// Parse a hexadecimal string to bytes
    /// </summary>
    /// <param name="hexadecimalString">
    /// The hexadecimal string, which must be an even number of characters.
    /// </param>
    /// <param name="value">
    /// The parsed value if successful.
    /// </param>
    /// <returns>
    /// True if successful.
    /// </returns>
    public static bool TryParse(string hexadecimalString, out byte[] value)
    {
        if (hexadecimalString.Length == 0)
        {
            value = Empty;
            return true;
        }

        if (hexadecimalString.Length % 2 != 0)
        {
            value = Empty;
            return false;
        }

        try
        {

            value = new byte[hexadecimalString.Length / 2];
            for (int i = 0, j = 0; j < hexadecimalString.Length; i++)
            {
                value[i] = (byte)((HexValues[hexadecimalString[j++] - '0'] << 4)
                                  | HexValues[hexadecimalString[j++] - '0']);
            }

            return true;
        }
        catch (OverflowException)
        {
            value = Empty;
            return false;
        }
    }
}

这个问题也可以使用查找表来解决。这将需要编码器和解码器的少量静态存储器。然而,这种方法很快:

编码器表512字节或1024字节(两次大小写(如果是大写和小写)需要)解码器表256字节或64 KiB(单个字符查找或双字符查找)

我的解决方案使用1024字节作为编码表,256字节用于解码。

解码

private static readonly byte[] LookupTable = new byte[] {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

private static byte Lookup(char c)
{
  var b = LookupTable[c];
  if (b == 255)
    throw new IOException("Expected a hex character, got " + c);
  return b;
}

public static byte ToByte(char[] chars, int offset)
{
  return (byte)(Lookup(chars[offset]) << 4 | Lookup(chars[offset + 1]));
}

编码

private static readonly char[][] LookupTableUpper;
private static readonly char[][] LookupTableLower;

static Hex()
{
  LookupTableLower = new char[256][];
  LookupTableUpper = new char[256][];
  for (var i = 0; i < 256; i++)
  {
    LookupTableLower[i] = i.ToString("x2").ToCharArray();
    LookupTableUpper[i] = i.ToString("X2").ToCharArray();
  }
}

public static char[] ToCharLower(byte[] b, int bOffset)
{
  return LookupTableLower[b[bOffset]];
}

public static char[] ToCharUpper(byte[] b, int bOffset)
{
  return LookupTableUpper[b[bOffset]];
}

比较

StringBuilderToStringFromBytes:   106148
BitConverterToStringFromBytes:     15783
ArrayConvertAllToStringFromBytes:  54290
ByteManipulationToCharArray:        8444
TableBasedToCharArray:              5651 *

*这个解决方案

Note

在解码过程中,可能会发生IOException和IndexOutOfRangeException(如果字符的值太高>256)。应该实现对流或数组进行去/编码的方法,这只是概念的证明。

您可以使用BitConverter.ToString方法:

byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}
Console.WriteLine( BitConverter.ToString(bytes));

输出:

00-01-02-04-08-10-20-40-80-FF

更多信息:BitConverter.ToString方法(Byte[])