如何将字节数组转换为十六进制字符串,反之亦然?
当前回答
另一种基于查找表的方法。该方法只为每个字节使用一个查找表,而不是为每个半字节使用查找表。
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;
}
其他回答
这是我的纯二进制解决方案,不需要库查找,也支持大写/小写:
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);
}
用@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…)。
Waleed Eissa代码的逆函数(十六进制字符串到字节数组):
public static byte[] HexToBytes(this string hexString)
{
byte[] b = new byte[hexString.Length / 2];
char c;
for (int i = 0; i < hexString.Length / 2; i++)
{
c = hexString[i * 2];
b[i] = (byte)((c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57)) << 4);
c = hexString[i * 2 + 1];
b[i] += (byte)(c < 0x40 ? c - 0x30 : (c < 0x47 ? c - 0x37 : c - 0x57));
}
return b;
}
Waleed Eissa功能,支持小写:
public static string BytesToHex(this byte[] barray, bool toLowerCase = true)
{
byte addByte = 0x37;
if (toLowerCase) addByte = 0x57;
char[] c = new char[barray.Length * 2];
byte b;
for (int i = 0; i < barray.Length; ++i)
{
b = ((byte)(barray[i] >> 4));
c[i * 2] = (char)(b > 9 ? b + addByte : b + 0x30);
b = ((byte)(barray[i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + addByte : b + 0x30);
}
return new string(c);
}
从微软的开发人员那里,一个很好的、简单的转换:
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代码。
// a safe version of the lookup solution:
public static string ByteArrayToHexViaLookup32Safe(byte[] bytes, bool withZeroX)
{
if (bytes.Length == 0)
{
return withZeroX ? "0x" : "";
}
int length = bytes.Length * 2 + (withZeroX ? 2 : 0);
StateSmall stateToPass = new StateSmall(bytes, withZeroX);
return string.Create(length, stateToPass, (chars, state) =>
{
int offset0x = 0;
if (state.WithZeroX)
{
chars[0] = '0';
chars[1] = 'x';
offset0x += 2;
}
Span<uint> charsAsInts = MemoryMarshal.Cast<char, uint>(chars.Slice(offset0x));
int targetLength = state.Bytes.Length;
for (int i = 0; i < targetLength; i += 1)
{
uint val = Lookup32[state.Bytes[i]];
charsAsInts[i] = val;
}
});
}
private struct StateSmall
{
public StateSmall(byte[] bytes, bool withZeroX)
{
Bytes = bytes;
WithZeroX = withZeroX;
}
public byte[] Bytes;
public bool WithZeroX;
}
推荐文章
- HTTP POST返回错误:417“期望失败。”
- 如何在。net中创建和使用资源
- 为什么Path。以Path.DirectorySeparatorChar开头的文件名合并不正确?
- 如何在c#中获得正确的时间戳
- 如何读一个文本文件到一个列表或数组与Python
- Linq选择列表中存在的对象(A,B,C)
- c# .NET中的App.config是什么?如何使用它?
- 如何在Python中将十六进制字符串转换为字节?
- c#:如何获得一个字符串的第一个字符?
- String类中的什么方法只返回前N个字符?
- 更好的方法将对象转换为int类型
- 我可以将c#字符串值转换为转义字符串文字吗?
- 在c#中转换char到int
- c#中朋友的对等物是什么?
- 关键字使用virtual+override vs. new