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


当前回答

从微软的开发人员那里,一个很好的、简单的转换:

public static string ByteArrayToString(byte[] ba) 
{
    // Concatenate the bytes into one long string
    return ba.Aggregate(new StringBuilder(32),
                            (sb, b) => sb.Append(b.ToString("X2"))
                            ).ToString();
}

虽然上面的内容简洁紧凑,但性能狂热者会使用枚举器对此尖叫不已。通过Tomalak原始答案的改进版本,您可以获得最佳性能:

public static string ByteArrayToString(byte[] ba)   
{   
   StringBuilder hex = new StringBuilder(ba.Length * 2);   

   for(int i=0; i < ba.Length; i++)       // <-- Use for loop is faster than foreach   
       hex.Append(ba[i].ToString("X2"));   // <-- ToString is faster than AppendFormat   

   return hex.ToString();   
} 

这是迄今为止我在这里看到的所有例程中速度最快的。不要只相信我的话…对每个例程进行性能测试并自行检查其CIL代码。

其他回答

可以使用从.NET 5开始的Convert.ToHexString。还有一个用于反向操作的方法:Convert.FromHexString。


对于较旧版本的.NET,您可以使用:

public static string ByteArrayToString(byte[] ba)
{
  StringBuilder hex = new StringBuilder(ba.Length * 2);
  foreach (byte b in ba)
    hex.AppendFormat("{0:x2}", b);
  return hex.ToString();
}

or:

public static string ByteArrayToString(byte[] ba)
{
  return BitConverter.ToString(ba).Replace("-","");
}

举个例子,这里有更多的方法。

反向转换如下:

public static byte[] StringToByteArray(String hex)
{
  int NumberChars = hex.Length;
  byte[] bytes = new byte[NumberChars / 2];
  for (int i = 0; i < NumberChars; i += 2)
    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  return bytes;
}

使用Substring是与Convert.ToByte结合使用的最佳选项。有关详细信息,请参阅此答案。如果需要更好的性能,必须避免Convert.ToByte,然后才能删除SubString。

这是我的纯二进制解决方案,不需要库查找,也支持大写/小写:

public static String encode(byte[] bytes, boolean uppercase) {
    char[] result = new char[2 * bytes.length];
    for (int i = 0; i < bytes.length; i++) {
        byte word = bytes[i];
        byte left = (byte) ((0XF0 & word) >>> 4);
        byte right = (byte) ((byte) 0X0F & word);

        int resultIndex = i * 2;
        result[resultIndex] = encode(left, uppercase);
        result[resultIndex + 1] = encode(right, uppercase);
    }
    return new String(result);
}

public static char encode(byte value, boolean uppercase) {
    int characterCase = uppercase ? 0 : 32;
    if (value > 15 || value < 0) {
        return '0';
    }
    if (value > 9) {
        return (char) (value + 0x37 | characterCase);
    }
    return (char) (value + 0x30);
}

扩展方法(免责声明:完全未经测试的代码,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的三种解决方案之一(最后一种是字符串上的扩展方法)。

测试:十六进制字符串到字节数组

我注意到,大多数测试都是在将Bytes数组转换为十六进制字符串的函数上执行的。因此,在这篇文章中,我将关注另一方面:将十六进制字符串转换为字节数组的函数。若您只对结果感兴趣,可以跳到“摘要”部分。测试代码文件在文章末尾提供。

标签

我想根据接受的答案(Tomalak)将函数命名为StringToByteArrayV1,或将其快捷到V1。其余函数将以相同的方式命名:V2、V3、V4、…、。。。,等

参与功能索引

Tomalak的StringToByteArrayV1(公认答案)Mykroft的StringToByteArrayV2(使用SoapHexBinary)drphrozen的StringToByteArrayV3(查找表)CoperNick的StringToByteArrayV4(字节操作)Chris F编写的StringToByteArrayV5_1(字节操作)Chris F的StringToByteArrayV5_2(V5_1+根据Amir Rezaei的评论修改)Chris F的StringToByteArrayV5_3(V5_2+根据Ben Voigt的评论对其进行了修改)(您可以在发布后的测试代码中看到它的最终形状)Ben Mosher编写的StringToByteArrayV6(字节操作)Maratius的StringToByteArrayV7(字节操作-安全版本)Maratius的StringToByteArrayV8(字节操作-不安全版本)StringToByteArrayV9(按Geograph)AlejandroAlis编写的StringToByteArrayV10Fredrik Hu编写的StringToByteArrayV11Maarten Bodewes编写的StringToByteArrayV12ClausAndersen编写的StringToByteArrayV13Stas Makutin编写的StringToByteArrayV14JJJ的StringToByteArrayV15JamieSee的StringToByteArrayV16spacepille的StringToByteArrayV17Gregory Morse编写的StringToByteArrayV18Rick编写的StringToByteArrayV19SandRock的StringToByteArrayV20Paul编写的StringToByteArrayV21

正确性测试

我通过传递1字节的所有256个可能值来测试正确性,然后检查输出是否正确。结果:

V18中以“00”开头的字符串有问题(请参阅Roger Stewart对此的评论)。除了通过所有测试。如果十六进制字符串字母是大写的:所有函数都成功传递如果十六进制字符串字母是小写的,则以下函数失败:V5_1、V5_2、v7、V8、V15、V19

注:V5_3解决了这个问题(V5_1和V5_2)

性能测试

我已经使用Stopwatch类进行了性能测试。

长字符串的性能

input length: 10,000,000 bytes
runs: 100
average elapsed time per run:
V1 = 136.4ms
V2 = 104.5ms
V3 = 22.0ms
V4 = 9.9ms
V5_1 = 10.2ms
V5_2 = 9.0ms
V5_3 = 9.3ms
V6 = 18.3ms
V7 = 9.8ms
V8 = 8.8ms
V9 = 10.2ms
V10 = 19.0ms
V11 = 12.2ms
V12 = 27.4ms
V13 = 21.8ms
V14 = 12.0ms
V15 = 14.9ms
V16 = 15.3ms
V17 = 9.5ms
V18 got excluded from this test, because it was very slow when using very long string
V19 = 222.8ms
V20 = 66.0ms
V21 = 15.4ms

V1 average ticks per run: 1363529.4
V2 is more fast than V1 by: 1.3 times (ticks ratio)
V3 is more fast than V1 by: 6.2 times (ticks ratio)
V4 is more fast than V1 by: 13.8 times (ticks ratio)
V5_1 is more fast than V1 by: 13.3 times (ticks ratio)
V5_2 is more fast than V1 by: 15.2 times (ticks ratio)
V5_3 is more fast than V1 by: 14.8 times (ticks ratio)
V6 is more fast than V1 by: 7.4 times (ticks ratio)
V7 is more fast than V1 by: 13.9 times (ticks ratio)
V8 is more fast than V1 by: 15.4 times (ticks ratio)
V9 is more fast than V1 by: 13.4 times (ticks ratio)
V10 is more fast than V1 by: 7.2 times (ticks ratio)
V11 is more fast than V1 by: 11.1 times (ticks ratio)
V12 is more fast than V1 by: 5.0 times (ticks ratio)
V13 is more fast than V1 by: 6.3 times (ticks ratio)
V14 is more fast than V1 by: 11.4 times (ticks ratio)
V15 is more fast than V1 by: 9.2 times (ticks ratio)
V16 is more fast than V1 by: 8.9 times (ticks ratio)
V17 is more fast than V1 by: 14.4 times (ticks ratio)
V19 is more SLOW than V1 by: 1.6 times (ticks ratio)
V20 is more fast than V1 by: 2.1 times (ticks ratio)
V21 is more fast than V1 by: 8.9 times (ticks ratio)

V18的长串性能

V18 took long time at the previous test, 
so let's decrease length for it:  
input length: 1,000,000 bytes
runs: 100
average elapsed time per run: V1 = 14.1ms , V18 = 146.7ms
V1 average ticks per run: 140630.3
V18 is more SLOW than V1 by: 10.4 times (ticks ratio)

短字符串的性能

input length: 100 byte
runs: 1,000,000
V1 average ticks per run: 14.6
V2 is more fast than V1 by: 1.4 times (ticks ratio)
V3 is more fast than V1 by: 5.9 times (ticks ratio)
V4 is more fast than V1 by: 15.7 times (ticks ratio)
V5_1 is more fast than V1 by: 15.1 times (ticks ratio)
V5_2 is more fast than V1 by: 18.4 times (ticks ratio)
V5_3 is more fast than V1 by: 16.3 times (ticks ratio)
V6 is more fast than V1 by: 5.3 times (ticks ratio)
V7 is more fast than V1 by: 15.7 times (ticks ratio)
V8 is more fast than V1 by: 18.0 times (ticks ratio)
V9 is more fast than V1 by: 15.5 times (ticks ratio)
V10 is more fast than V1 by: 7.8 times (ticks ratio)
V11 is more fast than V1 by: 12.4 times (ticks ratio)
V12 is more fast than V1 by: 5.3 times (ticks ratio)
V13 is more fast than V1 by: 5.2 times (ticks ratio)
V14 is more fast than V1 by: 13.4 times (ticks ratio)
V15 is more fast than V1 by: 9.9 times (ticks ratio)
V16 is more fast than V1 by: 9.2 times (ticks ratio)
V17 is more fast than V1 by: 16.2 times (ticks ratio)
V18 is more fast than V1 by: 1.1 times (ticks ratio)
V19 is more SLOW than V1 by: 1.6 times (ticks ratio)
V20 is more fast than V1 by: 1.9 times (ticks ratio)
V21 is more fast than V1 by: 11.4 times (ticks ratio)

测试代码

在使用以下代码之前,最好先阅读本文下面的免责声明部分https://github.com/Ghosticollis/performance-tests/blob/main/MTestPerformance.cs

总结

由于性能良好,我建议使用以下函数之一,并支持大写和小写:

CoperNick的StringToByteArrayV4StringToByteArrayV9(按Geograph)spacepille的StringToByteArrayV17StringToByteArrayV5_3基本上由Chris F开发(它基于V5_1,但我根据Amir Rezaei和Ben Voigt的评论对其进行了增强)。

以下是V5_3的最终形状:

static byte[] HexStringToByteArrayV5_3(string hexString) {
    int hexStringLength = hexString.Length;
    byte[] b = new byte[hexStringLength / 2];
    for (int i = 0; i < hexStringLength; i += 2) {
        int topChar = hexString[i];
        topChar = (topChar > 0x40 ? (topChar & ~0x20) - 0x37 : topChar - 0x30) << 4;
        int bottomChar = hexString[i + 1];
        bottomChar = bottomChar > 0x40 ? (bottomChar & ~0x20) - 0x37 : bottomChar - 0x30;
        b[i / 2] = (byte)(topChar + bottomChar);
    }
    return b;
}

免责声明

警告:我没有适当的测试知识。这些原始测试的主要目的是快速概述所有发布的函数的优点。如果您需要准确的结果,请使用适当的测试工具。

最后,我想说,我是新来的,在斯塔科弗洛活跃,如果我的职位空缺,我很抱歉。如果您能发表评论,我们将不胜感激。

对于Java 8,我们可以使用Byte.toUnsignedInt

public static String convertBytesToHex(byte[] bytes) {
    StringBuilder result = new StringBuilder();
    for (byte byt : bytes) {
        int decimal = Byte.toUnsignedInt(byt);
        String hex = Integer.toHexString(decimal);
        result.append(hex);
    }
    return result.toString();
}