C# 字节数组和十六进制字符串的相互转化
目录
C# 如何高效地实现字节数组 (byte array) 和十六进制字符串 (hex string) 的相互转化呢?
1 ByteArray 转 HexString
1.1 手动实现
public static string ByteArrayToHexString(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
1.2 手动实现优化
public static string ByteArrayToHexString(byte[] bytes)
{
char[] c = new char[bytes.Length * 2];
byte b;
for (int i = 0; i < bytes.Length; i++)
{
b = ((byte)(bytes[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(bytes[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
return new string(c);
}
1.3 使用 BitConverter
public static string ByteArrayToHexString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", "");
}
1.4 使用 Convert.ToHexString (限.NET 5+)
string hex = Convert.ToHexString(bytes);
1.5 使用 SoapHexBinary (限.NET Framework)
string hex = new SoapHexBinary(bytes).ToString();
1.6 性能测试与比较
我们使用 BenchmarkDotNet 在.NET 9.0 和.NET Framework 4.8 上对上述方法进行性能测试。
1.6.1 .NET 9.0
测试代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text;
namespace BenchMarkerNet9
{
public class Program
{
private static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarker>();
}
};
[MemoryDiagnoser]
public class Benchmarker
{
public byte[] bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
[Benchmark]
public string ByteArrayToHexString1()
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
[Benchmark]
public string ByteArrayToHexString2()
{
char[] c = new char[bytes.Length * 2];
byte b;
for (int i = 0; i < bytes.Length; i++)
{
b = ((byte)(bytes[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(bytes[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
return new string(c);
}
[Benchmark]
public string ByteArrayToHexString3()
{
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
[Benchmark]
public string ByteArrayToHexString4()
{
return Convert.ToHexString(bytes);
}
}
}
测试结果如下:
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|---------------------- |----------:|---------:|---------:|-------:|----------:|
| ByteArrayToHexString1 | 156.29 ns | 2.677 ns | 4.006 ns | 0.0429 | 808 B |
| ByteArrayToHexString2 | 22.63 ns | 0.475 ns | 0.650 ns | 0.0093 | 176 B |
| ByteArrayToHexString3 | 156.63 ns | 0.864 ns | 0.808 ns | 0.0110 | 208 B |
| ByteArrayToHexString4 | 11.45 ns | 0.262 ns | 0.408 ns | 0.0047 | 88 B |
- Mean : Arithmetic mean of all measurements 平均值
- Error : Half of 99.9% confidence interval 误差
- StdDev : Standard deviation of all measurements 方差
- Gen0 : GC Generation 0 collects per 1000 operations 第 0 代 GC 次数
- Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 分配的内存
- 1 ns : 1 Nanosecond (0.000000001 sec) 纳秒
可见,Convert.ToHexString 是最快的,手动实现优化次之,BitConverter 和手动实现性能很差。
1.6.2 .NET Framework 4.8
测试代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Runtime.Remoting.Metadata.W3cXsd2001;
using System.Text;
namespace BenchMarker480
{
public class Program
{
private static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarker>();
}
};
[MemoryDiagnoser]
public class Benchmarker
{
public byte[] bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
[Benchmark]
public string ByteArrayToHexString1()
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
[Benchmark]
public string ByteArrayToHexString2()
{
char[] c = new char[bytes.Length * 2];
byte b;
for (int i = 0; i < bytes.Length; i++)
{
b = ((byte)(bytes[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(bytes[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
return new string(c);
}
[Benchmark]
public string ByteArrayToHexString3()
{
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
[Benchmark]
public string ByteArrayToHexString4()
{
return new SoapHexBinary(bytes).ToString();
}
}
}
测试结果如下:
| Method | Mean | Error | StdDev | Median | Gen0 | Allocated |
|---------------------- |----------:|----------:|----------:|----------:|-------:|----------:|
| ByteArrayToHexString1 | 539.00 ns | 22.907 ns | 67.543 ns | 513.00 ns | 0.1035 | 545 B |
| ByteArrayToHexString2 | 37.79 ns | 0.656 ns | 0.613 ns | 37.91 ns | 0.0297 | 156 B |
| ByteArrayToHexString3 | 194.82 ns | 2.988 ns | 3.557 ns | 194.53 ns | 0.0565 | 296 B |
| ByteArrayToHexString4 | 339.67 ns | 5.352 ns | 4.745 ns | 340.74 ns | 0.1135 | 597 B |
可见,手动实现优化是最快的,BitConverter 次之,SoapHexBinary 和手动实现性能很差。
1.7 结论
Convert.ToHexString是最快的,假设你的项目是.NET 5+,推荐使用。- 如果你的项目是.NET 5 以下,推荐使用
手动实现优化。 - 纵向对比会发现,
.NET 9的StringBuilder和BitConverter性能比.NET framework 4.8有较大提升。
1.8 注意
- byte 数组有一个扩展方法
ToString(),但是它返回的是"System.Byte[]"。这是因为System.Byte继承自System.Object,而System.Object有一个ToString()方法,且System.Byte没有重写这个方法。同样,Console.WriteLine(bytes)也会返回System.Byte[]。 - 不可以使用
Encoding.xxx.GetString(bytes),因为这个方法会将字节数组按照指定的编码转换成字符串,而不是转换为十六进制字符串。 - 将 byte 数组写入 json 或其他文本文件时,建议使用
Convert.ToBase64String(bytes),不建议使用 hex string。
2 HexString 转 ByteArray
2.1 手动实现
public static byte[] HexStringToByteArray(string hex)
{
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
2.2 基于 Linq 的实现
public static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
2.3 使用 Convert.FromHexString (限.NET 5+)
public static byte[] HexStringToByteArray(string hex)
{
return Convert.FromHexString(hex);
}
2.4 使用 SoapHexBinary (限.NET Framework)
public static byte[] HexStringToByteArray(string hex)
{
return new SoapHexBinary(hex).Value;
}
2.5 性能测试与比较
环境同上。
2.5.1 .NET 9.0
测试代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace BenchMarkerNet9
{
public class Program
{
private static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarker>();
}
};
[MemoryDiagnoser]
public class Benchmarker
{
public string hex = "0102030405060708090A0B0C0D0E0F10";
[Benchmark]
public byte[] HexToBytes1()
{
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
[Benchmark]
public byte[] HexToBytes2()
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
[Benchmark]
public byte[] HexToBytes3()
{
return Convert.FromHexString(hex);
}
}
}
测试结果如下:
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|------------ |-----------:|----------:|-----------:|-------:|----------:|
| HexToBytes1 | 172.904 ns | 8.5000 ns | 25.0625 ns | 0.0293 | 552 B |
| HexToBytes2 | 200.896 ns | 3.9337 ns | 5.7660 ns | 0.0410 | 776 B |
| HexToBytes3 | 7.851 ns | 0.1848 ns | 0.2270 ns | 0.0021 | 40 B |
可见,Convert.FromHexString 是最快的,手动实现和基于Linq的实现性能很差。
2.5.2 .NET Framework 4.8
测试代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Linq;
using System.Runtime.Remoting.Metadata.W3cXsd2001;
namespace BenchMarker480
{
public class Program
{
private static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarker>();
}
};
[MemoryDiagnoser]
public class Benchmarker
{
public string hex = "0102030405060708090A0B0C0D0E0F10";
[Benchmark]
public byte[] HexToBytes1()
{
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
[Benchmark]
public byte[] HexToBytes2()
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
[Benchmark]
public byte[] HexToBytes3()
{
return SoapHexBinary.Parse(hex).Value;
}
}
}
测试结果如下:
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|------------ |-----------:|---------:|---------:|-------:|----------:|
| HexToBytes1 | 414.5 ns | 13.73 ns | 40.48 ns | 0.0663 | 349 B |
| HexToBytes2 | 586.6 ns | 3.08 ns | 2.57 ns | 0.0992 | 525 B |
| HexToBytes3 | 1,285.0 ns | 18.47 ns | 17.27 ns | 0.3071 | 1610 B |
可见,手动实现是最快的,基于Linq的实现次之,SoapHexBinary 性能很差。
2.6 结论
Convert.FromHexString是最快的,假设你的项目是.NET 5+,推荐使用。- 如果你的项目是.NET 5 以下,推荐使用
手动实现。 - 纵向对比会发现,
.NET 9的手动实现性能和Linq性能比.NET framework 4.8有较大提升。