如何将 byte[]
转换为 string
?每次尝试时,我都会得到
System.Byte[]
而不是值。
此外,如何获取十六进制值而不是十进制值?
有一个内置的方法可以做到这一点:
byte[] data = { 1, 2, 4, 8, 16, 32 };
string hex = BitConverter.ToString(data);
结果:01-02-04-08-10-20
如果您想去掉破折号,只需将它们删除即可:
string hex = BitConverter.ToString(data).Replace("-", string.Empty);
结果:010204081020
如果您想要更紧凑的表示形式,您可以使用Base64:
string base64 = Convert.ToBase64String(data);
结果:AQIECBAg
+=
来连接字符串,是吗? - Guffa+=
对于非常短的循环来说效果很好,但是它的扩展性非常差。每多一个迭代,执行时间大致会加倍,因此在大约20次迭代时就会出现性能问题。每增加10个迭代将使循环时间变慢约1000倍。 - Guffa感谢 @antoninkriz 的 基准比较,我们可以看到 Convert.ToHexString
今天是明显的赢家
如果你不使用 Convert.ToHexString
,那么你会很愚蠢。在我看来,它在可读性、性能和安全性方面都表现出色。
以下内容摘自2012年4月10日之前:
我想试图比较这里列出的每种方法的速度,只是为了好玩。我基于这个速度测试代码。
结果表明, BitConverter+String.Replace 似乎比大多数其他简单的方式更快。但是,采用像 Nathan Moinvaziri的ByteArrayToHexString 或 Kurt's ToHex 这样的算法可以提高速度。
我还发现有趣的是,在长字符串上,string.Concat 和 string.Join 的速度比StringBuilder实现慢得多,但对于较短的数组来说则相似。这可能是由于StringBuilder在长字符串上会扩展,因此设置初始大小应该能抵消这种差异。
Convert.ToHexString
时)Convert.ToHexString
时)LONG_STRING_LENGTH = 1000 * 1024;
LONG_STRING_LENGTH = 100 * 1024;
,结果类似
int MANY_STRING_COUNT = 1000;
int MANY_STRING_LENGTH = 1024;
(与第一个测试中的字节数相同,但在不同的数组中)
int MANY_STRING_COUNT = 2000;
int MANY_STRING_LENGTH = 20;
我使用的测试代码:
void Main()
{
int LONG_STRING_LENGTH = 100 * 1024;
int MANY_STRING_COUNT = 1024;
int MANY_STRING_LENGTH = 100;
var source = GetRandomBytes(LONG_STRING_LENGTH);
List<byte[]> manyString = new List<byte[]>(MANY_STRING_COUNT);
for (int i = 0; i < MANY_STRING_COUNT; ++i)
{
manyString.Add(GetRandomBytes(MANY_STRING_LENGTH));
}
var algorithms = new Dictionary<string,Func<byte[], string>>();
algorithms["BitConvertReplace"] = BitConv;
algorithms["StringBuilder"] = StringBuilderTest;
algorithms["LinqConcat"] = LinqConcat;
algorithms["LinqJoin"] = LinqJoin;
algorithms["LinqAgg"] = LinqAgg;
algorithms["ToHex"] = ToHex;
algorithms["ByteArrayToHexString"] = ByteArrayToHexString;
Console.WriteLine(" === Long string test");
foreach (var pair in algorithms) {
TimeAction(pair.Key + " calculation", 500, () =>
{
pair.Value(source);
});
}
Console.WriteLine(" === Many string test");
foreach (var pair in algorithms) {
TimeAction(pair.Key + " calculation", 500, () =>
{
foreach (var str in manyString)
{
pair.Value(str);
}
});
}
}
// Define other methods and classes here
static void TimeAction(string description, int iterations, Action func) {
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < iterations; i++) {
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
//static byte[] GetRandomBytes(int count) {
// var bytes = new byte[count];
// (new Random()).NextBytes(bytes);
// return bytes;
//}
static Random rand = new Random();
static byte[] GetRandomBytes(int count) {
var bytes = new byte[count];
rand.NextBytes(bytes);
return bytes;
}
static string BitConv(byte[] data)
{
return BitConverter.ToString(data).Replace("-", string.Empty);
}
static string StringBuilderTest(byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length*2);
foreach (byte b in data)
sb.Append(b.ToString("X2"));
return sb.ToString();
}
static string LinqConcat(byte[] data)
{
return string.Concat(data.Select(b => b.ToString("X2")).ToArray());
}
static string LinqJoin(byte[] data)
{
return string.Join("",
data.Select(
bin => bin.ToString("X2")
).ToArray());
}
static string LinqAgg(byte[] data)
{
return data.Aggregate(new StringBuilder(),
(sb,v)=>sb.Append(v.ToString("X2"))
).ToString();
}
static string ToHex(byte[] bytes)
{
char[] c = new char[bytes.Length * 2];
byte b;
for(int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
{
b = ((byte)(bytes[bx] >> 4));
c[cx] = (char)(b > 9 ? b - 10 + 'A' : b + '0');
b = ((byte)(bytes[bx] & 0x0F));
c[++cx] = (char)(b > 9 ? b - 10 + 'A' : b + '0');
}
return new string(c);
}
public static string ByteArrayToHexString(byte[] Bytes)
{
StringBuilder Result = new StringBuilder(Bytes.Length*2);
string HexAlphabet = "0123456789ABCDEF";
foreach (byte B in Bytes)
{
Result.Append(HexAlphabet[(int)(B >> 4)]);
Result.Append(HexAlphabet[(int)(B & 0xF)]);
}
return Result.ToString();
}
还有另一个类似过程的答案, 我还没有比较我们的结果。
(char)(b > 9 ? b - 10 + 'A' : b + '0');
<-- 这也更容易通过将'A'变成'a'来改变大写字母为小写字母 - user34537"0123456789ABCDEF"[b]
- Jonathan GilbertByteArrayToHexString
方法的不完整版本,这个方法是我的总结推荐用于提高速度的。 - ThymineConvert.ToHexString
和其他一些方法:https://dev59.com/Y3VC5IYBdhLWcg3wYQEp#71904920 - antoninkriz十六进制,Linq-fu:
string.Concat(ba.Select(b => b.ToString("X2")).ToArray())
跟上时代的更新
正如@RubenBartelink所指出的那样,在4.0之前,没有将IEnumerable<string>
转换为数组的代码:ba.Select(b => b.ToString("X2"))
是无法工作的,但现在同样的代码可以在4.0上运行。
这段代码...
byte[] ba = { 1, 2, 4, 8, 16, 32 };
string s = string.Concat(ba.Select(b => b.ToString("X2")));
string t = string.Concat(ba.Select(b => b.ToString("X2")).ToArray());
Console.WriteLine (s);
Console.WriteLine (t);
在 .NET 4.0 之前,输出结果为:
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[System.Byte,System.String]
010204081020
从.NET 4.0开始,string.Concat有一个重载可以接受IEnumerable。因此,在4.0上,以上代码对于变量s和t将具有相同的输出。
010204081020
010204081020
在4.0版本之前,ba.Select(b => b.ToString("X2"))
会使用重载 (object arg0)
。为了让 IEnumerable<string>
调用正确的重载函数,即 (params string[] values)
,我们需要将 IEnumerable<string>
转换为字符串数组。在4.0版本之前,string.Concat有10个重载函数,但是在4.0版本中增加到了12个。
string.Concat(from b in ba select b.ToString("X2"))
更漂亮 - 您能否将其作为第二行进行编辑,并提供它依赖于.NET 4.0的String.Concat(IEnumerable<string>)
重载的条件? - Ruben Bartelinkpublic static string ByteArrayToHexString(byte[] Bytes)
{
StringBuilder Result = new StringBuilder(Bytes.Length * 2);
string HexAlphabet = "0123456789ABCDEF";
foreach (byte B in Bytes)
{
Result.Append(HexAlphabet[(int)(B >> 4)]);
Result.Append(HexAlphabet[(int)(B & 0xF)]);
}
return Result.ToString();
}
public static byte[] HexStringToByteArray(string Hex)
{
byte[] Bytes = new byte[Hex.Length / 2];
int[] HexValue = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1)
{
Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
}
return Bytes;
}
另外,您还可以像这样预先构建翻译表格,以实现更快的结果:
StringBuilder Result = new StringBuilder(Bytes.Length * 2);
会使这个过程稍微快一点。 - Thymine我喜欢使用扩展方法进行转换,即使它们只是包装标准库方法。在十六进制转换的情况下,我使用以下手动调整(即快速)算法:
public static string ToHex(this byte[] bytes)
{
char[] c = new char[bytes.Length * 2];
byte b;
for(int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
{
b = ((byte)(bytes[bx] >> 4));
c[cx] = (char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30);
b = ((byte)(bytes[bx] & 0x0F));
c[++cx]=(char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30);
}
return new string(c);
}
public static byte[] HexToBytes(this string str)
{
if (str.Length == 0 || str.Length % 2 != 0)
return new byte[0];
byte[] buffer = new byte[str.Length / 2];
char c;
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
{
// Convert first half of byte
c = str[sx];
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4);
// Convert second half of byte
c = str[++sx];
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0'));
}
return buffer;
}
好的,我不经常将字节转换为十六进制,所以我必须说我不知道是否有比这更好的方法,但是下面是一种方法。
StringBuilder sb = new StringBuilder();
foreach (byte b in myByteArray)
sb.Append(b.ToString("X2"));
string hexString = sb.ToString();
非常快的扩展方法(包括反转):
public static class ExtensionMethods {
public static string ToHex(this byte[] data) {
return ToHex(data, "");
}
public static string ToHex(this byte[] data, string prefix) {
char[] lookup = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
int i = 0, p = prefix.Length, l = data.Length;
char[] c = new char[l * 2 + p];
byte d;
for(; i < p; ++i) c[i] = prefix[i];
i = -1;
--l;
--p;
while(i < l) {
d = data[++i];
c[++p] = lookup[d >> 4];
c[++p] = lookup[d & 0xF];
}
return new string(c, 0, c.Length);
}
public static byte[] FromHex(this string str) {
return FromHex(str, 0, 0, 0);
}
public static byte[] FromHex(this string str, int offset, int step) {
return FromHex(str, offset, step, 0);
}
public static byte[] FromHex(this string str, int offset, int step, int tail) {
byte[] b = new byte[(str.Length - offset - tail + step) / (2 + step)];
byte c1, c2;
int l = str.Length - tail;
int s = step + 1;
for(int y = 0, x = offset; x < l; ++y, x += s) {
c1 = (byte)str[x];
if(c1 > 0x60) c1 -= 0x57;
else if(c1 > 0x40) c1 -= 0x37;
else c1 -= 0x30;
c2 = (byte)str[++x];
if(c2 > 0x60) c2 -= 0x57;
else if(c2 > 0x40) c2 -= 0x37;
else c2 -= 0x30;
b[y] = (byte)((c1 << 4) + c2);
}
return b;
}
}
在上面的速度测试中,ToHexPatrick计算在长字符串测试和多字符串测试中都比其他方法快。
=== 长字符串测试
BitConvertReplace计算时间为2415毫秒
StringBuilder计算时间为5668毫秒
LinqConcat计算时间为11826毫秒
LinqJoin计算时间为9323毫秒
LinqAgg计算时间为7444毫秒
ToHexTable计算时间为1028毫秒
ToHexAcidzombie计算时间为1035毫秒
ToHexPatrick计算时间为814毫秒
ToHexKurt计算时间为1604毫秒
ByteArrayToHexString计算时间为1330毫秒=== 多字符串测试
BitConvertReplace计算时间为2238毫秒
StringBuilder计算时间为5393毫秒
LinqConcat计算时间为9043毫秒
LinqJoin计算时间为9131毫秒
LinqAgg计算时间为7324毫秒
ToHexTable计算时间为968毫秒
ToHexAcidzombie计算时间为969毫秒
ToHexPatrick计算时间为956毫秒
ToHexKurt计算时间为1547毫秒
ByteArrayToHexString计算时间为1277毫秒
我想我应该提供一个答案。从我的测试来看,这种方法是最快的。
public static class Helper
{
public static string[] HexTbl = Enumerable.Range(0, 256).Select(v => v.ToString("X2")).ToArray();
public static string ToHex(this IEnumerable<byte> array)
{
StringBuilder s = new StringBuilder();
foreach (var v in array)
s.Append(HexTbl[v]);
return s.ToString();
}
public static string ToHex(this byte[] array)
{
StringBuilder s = new StringBuilder(array.Length*2);
foreach (var v in array)
s.Append(HexTbl[v]);
return s.ToString();
}
}
再添加一个答案,有一个 System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary
类可以将字节转换为十六进制并反之:
string hex = new SoapHexBinary(bytes).ToString();
byte[] bytes = SoapHexBinary.Parse(hex).Value;
不确定它与其他实现相比如何(基准测试),但在我看来,它非常简单--特别是将十六进制转换回字节时。
使用:
byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x0D, 0x0E, 0x0F };
string hex = string.Empty;
data.ToList().ForEach(b => hex += b.ToString("x2"));
// use "X2" for uppercase hex letters
Console.WriteLine(hex);
Result: 0102030d0e0f