如何将字节数组转换为十六进制字符串,反之亦然?
当前回答
另一种方法是使用stackaloc来降低GC内存压力:
static string ByteToHexBitFiddle(byte[] bytes)
{
var c = stackalloc char[bytes.Length * 2 + 1];
int b;
for (int i = 0; i < bytes.Length; ++i)
{
b = bytes[i] >> 4;
c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7));
b = bytes[i] & 0xF;
c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7));
}
c[bytes.Length * 2 ] = '\0';
return new string(c);
}
其他回答
就速度而言,这似乎比这里的任何东西都要好:
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);
}
这是我的纯二进制解决方案,不需要库查找,也支持大写/小写:
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);
}
可以使用从.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。
有一个简单的一行解决方案尚未提及,它将十六进制字符串转换为字节数组(我们不在乎这里的否定解释,因为这无关紧要):
BigInteger.Parse(str, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray();
另一种基于查找表的方法。该方法只为每个字节使用一个查找表,而不是为每个半字节使用查找表。
private static readonly uint[] _lookup32 = CreateLookup32();
private static uint[] CreateLookup32()
{
var result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s=i.ToString("X2");
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
}
return result;
}
private static string ByteArrayToHexViaLookup32(byte[] bytes)
{
var lookup32 = _lookup32;
var result = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++)
{
var val = lookup32[bytes[i]];
result[2*i] = (char)val;
result[2*i + 1] = (char) (val >> 16);
}
return new string(result);
}
我还使用查找表中的ushort、struct{char X1,X2}、struct{byte X1,X2}测试了这个变体。
根据编译目标(x86、X64)的不同,它们要么具有大致相同的性能,要么稍慢于此变体。
为了获得更高的性能,其不安全的兄弟:
private static readonly uint[] _lookup32Unsafe = CreateLookup32Unsafe();
private static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_lookup32Unsafe,GCHandleType.Pinned).AddrOfPinnedObject();
private static uint[] CreateLookup32Unsafe()
{
var result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s=i.ToString("X2");
if(BitConverter.IsLittleEndian)
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
else
result[i] = ((uint)s[1]) + ((uint)s[0] << 16);
}
return result;
}
public static string ByteArrayToHexViaLookup32Unsafe(byte[] bytes)
{
var lookupP = _lookup32UnsafeP;
var result = new char[bytes.Length * 2];
fixed(byte* bytesP = bytes)
fixed (char* resultP = result)
{
uint* resultP2 = (uint*)resultP;
for (int i = 0; i < bytes.Length; i++)
{
resultP2[i] = lookupP[bytesP[i]];
}
}
return new string(result);
}
或者如果您认为可以直接写入字符串:
public static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes)
{
var lookupP = _lookup32UnsafeP;
var result = new string((char)0, bytes.Length * 2);
fixed (byte* bytesP = bytes)
fixed (char* resultP = result)
{
uint* resultP2 = (uint*)resultP;
for (int i = 0; i < bytes.Length; i++)
{
resultP2[i] = lookupP[bytesP[i]];
}
}
return result;
}
推荐文章
- net HttpClient。如何POST字符串值?
- 我如何使一个方法的返回类型泛型?
- 何时处理CancellationTokenSource?
- 如何获取正在执行的程序集版本?
- AutoMapper vs valueinjector
- 为什么控制台不。Writeline,控制台。在Visual Studio Express中编写工作?
- 什么是.NET程序集?
- 字符串不能识别为有效的日期时间“格式dd/MM/yyyy”
- 函数应该返回空对象还是空对象?
- 如何转换日期时间?将日期时间
- 如何在c#中连接列表?
- 在c#中引用类型变量的“ref”的用途是什么?
- 在JavaScript中根据键值查找和删除数组中的对象
- 防止在ASP中缓存。NET MVC中使用属性的特定操作
- 如何确定一个数组是否包含另一个数组的所有元素