如何将字节数组转换为十六进制字符串,反之亦然?
当前回答
如果您希望比BitConverter更灵活,但不希望使用那些笨重的90年代风格的显式循环,那么您可以这样做:
String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));
或者,如果您使用的是.NET 4.0:
String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));
(后者来自对原帖子的评论。)
其他回答
具有扩展支持的基本解决方案
public static class Utils
{
public static byte[] ToBin(this 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;
}
public static string ToHex(this byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
}
并像下面那样使用这个类
byte[] arr1 = new byte[] { 1, 2, 3 };
string hex1 = arr1.ToHex();
byte[] arr2 = hex1.ToBin();
这里不想赘述很多答案,但我发现了一个十六进制字符串解析器的相当优化(比公认的好4.5倍)、简单的实现。首先,我的测试输出(第一批是我的实现):
Give me that string:
04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939f
Time to parse 100,000 times: 50.4192 ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F
Accepted answer: (StringToByteArray)
Time to parse 100000 times: 233.1264ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F
With Mono's implementation:
Time to parse 100000 times: 777.2544ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F
With SoapHexBinary:
Time to parse 100000 times: 845.1456ms
Result as base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=
BitConverter'd: 04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F
base64和“BitConverter'd”行用于测试正确性。请注意,它们是相等的。
实施:
public static byte[] ToByteArrayFromHex(string hexString)
{
if (hexString.Length % 2 != 0) throw new ArgumentException("String must have an even length");
var array = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
array[i/2] = ByteFromTwoChars(hexString[i], hexString[i + 1]);
}
return array;
}
private static byte ByteFromTwoChars(char p, char p_2)
{
byte ret;
if (p <= '9' && p >= '0')
{
ret = (byte) ((p - '0') << 4);
}
else if (p <= 'f' && p >= 'a')
{
ret = (byte) ((p - 'a' + 10) << 4);
}
else if (p <= 'F' && p >= 'A')
{
ret = (byte) ((p - 'A' + 10) << 4);
} else throw new ArgumentException("Char is not a hex digit: " + p,"p");
if (p_2 <= '9' && p_2 >= '0')
{
ret |= (byte) ((p_2 - '0'));
}
else if (p_2 <= 'f' && p_2 >= 'a')
{
ret |= (byte) ((p_2 - 'a' + 10));
}
else if (p_2 <= 'F' && p_2 >= 'A')
{
ret |= (byte) ((p_2 - 'A' + 10));
} else throw new ArgumentException("Char is not a hex digit: " + p_2, "p_2");
return ret;
}
我尝试了一些不安全的东西,并将(显然是冗余的)字符移动到另一个方法来蚕食if序列,但这是最快的。
(我承认这回答了一半的问题。我觉得字符串->字节[]转换不足,而字节[]->字符串角度似乎被很好地覆盖了。因此,这个答案。)
扩展BigInteger方法(Gregory Morse在上面提到过)。我不能评论效率,它使用System.Linq.Reverse(),但它很小而且内置。
// To hex
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("Test String!£");
string hexString = new System.Numerics.BigInteger(bytes.Reverse().ToArray()).ToString("x2");
// From hex
byte[] fromHexBytes = System.Numerics.BigInteger.Parse(hexString, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray();
// Unit test
CollectionAssert.AreEqual(bytes, fromHexBytes);
// 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;
}
如果您希望比BitConverter更灵活,但不希望使用那些笨重的90年代风格的显式循环,那么您可以这样做:
String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));
或者,如果您使用的是.NET 4.0:
String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));
(后者来自对原帖子的评论。)
推荐文章
- 实体框架核心:在上一个操作完成之前,在此上下文中开始的第二个操作
- 如何为构造函数定制Visual Studio的私有字段生成快捷方式?
- 如何使用JSON确保字符串是有效的JSON。网
- 使用C返回一个数组
- AppSettings从.config文件中获取值
- 通过HttpClient向REST API发布一个空体
- 如何检查IEnumerable是否为空或空?
- 自动化invokerrequired代码模式
- 在c#代码中设置WPF文本框的背景颜色
- 在c#中,什么是单子?
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 向对象数组添加属性
- c#线程安全快速(est)计数器
- 如何将此foreach代码转换为Parallel.ForEach?
- 如何分裂()一个分隔字符串到一个列表<字符串>