375

How do I convert a byte[] to a string? Every time I attempt it, I get

System.Byte[]

instead of the value.

Also, how do I get the value in Hex instead of a decimal?

user229044
  • 232,980
  • 40
  • 330
  • 338

19 Answers19

698

There is a built in method for this:

byte[] data = { 1, 2, 4, 8, 16, 32 };

string hex = BitConverter.ToString(data);

Result: 01-02-04-08-10-20

If you want it without the dashes, just remove them:

string hex = BitConverter.ToString(data).Replace("-", string.Empty);

Result: 010204081020

If you want a more compact representation, you can use Base64:

string base64 = Convert.ToBase64String(data);

Result: AQIECBAg

Levi Botelho
  • 24,626
  • 5
  • 61
  • 96
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • how to convert back from Base64? i.e. the inverse of string base64 = Convert.ToBase64String(data); – ala Jul 28 '09 at 12:18
  • 9
    nevermind, i think i found it Convert.FromBase64String(..) – ala Jul 28 '09 at 12:25
  • 1
    Just in case, for Windows 8/Windows Phone 8.1 there is an [CryptographicBuffer.EncodeToHexString](http://msdn.microsoft.com/en-us/library/windows.security.cryptography.cryptographicbuffer.encodetohexstring.aspx) for case without dashes. – Sevenate Nov 13 '14 at 18:48
  • I was trying all of these other ways manually converting each character to a string, and it was taking forever. I used the `Convert.ToBase64String(data)` and `Convert.FromBase64String(data)` functions, and it was almost instant. It's on the scale of millions of times faster. – Grungondola Nov 23 '15 at 20:48
  • 2
    @Grungondola: It sounds strange that all other ways of converting bytes to a string would be so much slower. You weren't using `+=` to concatenate the strings together, were you? – Guffa Nov 23 '15 at 22:29
  • @Guffa, yes. I use `+=` all the time. – Grungondola Nov 24 '15 at 14:20
  • 4
    @Grungondola: That is most likely the reason for the bad performance. If you use `+=` in a loop it works well for very short loops, but it scales very badly. Each additional iteration roughly doubles the execution time, so at about 20 iterations you have a performance issue. Every additional 10 iterations will make the loop take about 1000 times longer. – Guffa Nov 24 '15 at 16:19
  • @Guffa Ah, so the close to 2 million iterations that I needed explains the duration. It did end up using a lot of system memory to maintain. Thank you for this information. – Grungondola Nov 24 '15 at 16:27
  • 1
    [Allow BitConverter.ToString to format without dashes · Issue #519 · dotnet/corefx](https://github.com/dotnet/corefx/issues/519 ) – lindexi Mar 18 '19 at 08:00
  • 1
    It looks like System.Convert.ToHexString(byte[]) is now a thing: (https://github.com/dotnet/runtime/pull/37546) – brenthompson2 Jul 21 '20 at 15:04
  • ^ Not aval on .net 4.7.x – Kush Aug 24 '20 at 12:29
108

.Net5.0 Update

Thanks to @antoninkriz's benchmark comparison we can see that Convert.ToHexString is by far the clear winner today

You'd be foolish to use anything other than Convert.ToHexString. In my opinion it clearly wins in: readability, performance, safety


The rest of this is from before Apr 10, 2012:

I thought I would attempt to compare the speed of each of the methods listed here for the hell of it. I based the speed testing code off this.

The result is that BitConverter+String.Replace seems to be faster than most other simple ways. But the speed can be improved with algorithms like Nathan Moinvaziri's ByteArrayToHexString or Kurt's ToHex.

I also found it interesting that string.Concat and string.Join are much slower than StringBuilder implementations for long strings, but similar for shorter arrays. Probably due to expanding the StringBuilder on the longer strings, so setting the initial size should negate this difference.

  • Took each bit of code from an answer here:
  • BitConvertRep = Answer by Guffa, BitConverter and String.Replace (I'd recommend for most cases, [edit:] where you can't use Convert.ToHexString)
  • StringBuilder = Answer by Quintin Robinson, foreach char StringBuilder.Append
  • LinqConcat = Answer by Michael Buen, string.Concat of Linq built array
  • LinqJoin = Answer by mloskot, string.Join of Linq built array
  • LinqAgg = Answer by Matthew Whited, IEnumerable.Aggregate with StringBuilder
  • ToHex = Answer by Kurt, sets chars in an array, using byte values to get hex
  • ByteArrayToHexString = Answer by Nathan Moinvaziri, approx same speed as the ToHex above, and is probably easier to read (I'd recommend for speed, [edit:] where you can't use Convert.ToHexString)
  • ToHexFromTable = Linked in answer by Nathan Moinvaziri, for me this is near the same speed as the above 2 but requires an array of 256 strings to always exist

With: LONG_STRING_LENGTH = 1000 * 1024;

  • BitConvertRep calculation Time Elapsed 27,202 ms (fastest built in/simple)
  • StringBuilder calculation Time Elapsed 75,723 ms (StringBuilder no reallocate)
  • LinqConcat calculation Time Elapsed 182,094 ms
  • LinqJoin calculation Time Elapsed 181,142 ms
  • LinqAgg calculation Time Elapsed 93,087 ms (StringBuilder with reallocating)
  • ToHex calculation Time Elapsed 19,167 ms (fastest)

With: LONG_STRING_LENGTH = 100 * 1024;, Similar results

  • BitConvertReplace calculation Time Elapsed 3431 ms
  • StringBuilder calculation Time Elapsed 8289 ms
  • LinqConcat calculation Time Elapsed 21512 ms
  • LinqJoin calculation Time Elapsed 19433 ms
  • LinqAgg calculation Time Elapsed 9230 ms
  • ToHex calculation Time Elapsed 1976 ms

With: int MANY_STRING_COUNT = 1000; int MANY_STRING_LENGTH = 1024; (Same byte count as first test but in different arrays)

  • BitConvertReplace calculation Time Elapsed 25,680 ms
  • StringBuilder calculation Time Elapsed 78,411 ms
  • LinqConcat calculation Time Elapsed 101,233 ms
  • LinqJoin calculation Time Elapsed 99,311 ms
  • LinqAgg calculation Time Elapsed 84,660 ms
  • ToHex calculation Time Elapsed 18,221 ms

With: int MANY_STRING_COUNT = 2000; int MANY_STRING_LENGTH = 20;

  • BitConvertReplace calculation Time Elapsed 1347 ms
  • StringBuilder calculation Time Elapsed 3234 ms
  • LinqConcat calculation Time Elapsed 5013 ms
  • LinqJoin calculation Time Elapsed 4826 ms
  • LinqAgg calculation Time Elapsed 3589 ms
  • ToHex calculation Time Elapsed 772 ms

Testing code I used:

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();
}

Also another answer with a similar process, I haven't compared our results yet.

Thymine
  • 8,775
  • 2
  • 35
  • 47
  • Good answer but one line of your code irritates me. Why do you use hex values instead of chars? Why didnt you do this `(char)(b > 9 ? b - 10 + 'A' : b + '0');` <-- also easier to change upper to lowercase by making 'A' into 'a' –  Apr 06 '12 at 22:22
  • 1
    Honestly, I just copied that part from [Kurt's Answer](http://stackoverflow.com/a/3974535/356218) and didn't even bother figuring out exactly how it was working at the time... – Thymine Apr 09 '12 at 16:44
  • 1
    Even better performance: `"0123456789ABCDEF"[b]` – Jonathan Gilbert Jan 13 '17 at 16:21
  • @JonathanGilbert that looks like an incomplete version of the method `ByteArrayToHexString` above, which my summary recommends for speed – Thymine Jan 16 '17 at 19:14
  • Oh, you're right, for some reason I didn't see that variant. Not sure how I missed it. :-) – Jonathan Gilbert Jan 16 '17 at 20:43
  • 1
    Just FYI I've posted updated benchmark with `Convert.ToHexString` and some other methods here: https://stackoverflow.com/a/71904920/3161322 – antoninkriz Apr 17 '22 at 19:45
81

Hex, Linq-fu:

string.Concat(ba.Select(b => b.ToString("X2")).ToArray())

UPDATE with the times

As noted by @RubenBartelink, the code that don't have a conversion of IEnumerable<string> to an array: ba.Select(b => b.ToString("X2")) does not work prior to 4.0, the same code is now working on 4.0.

This code...

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);

...prior to .NET 4.0, the output is:

System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[System.Byte,System.String]
010204081020

On .NET 4.0 onwards, string.Concat has an overload that accepts IEnumerable. Hence on 4.0, the above code will have same output for both variables s and t

010204081020
010204081020

Prior to 4.0, ba.Select(b => b.ToString("X2")) goes to overload (object arg0), the way for the IEnumerable<string> to go to a proper overload, i.e. (params string[] values), is we need to convert the IEnumerable<string> to string array. Prior to 4.0, string.Concat has 10 overload functions, on 4.0 it is now 12

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • Michael, you need a .ToArray() on the Select, otherwise (as presented) you get a {System.Linq.Enumerable.WhereSelectArrayIterator} which String.Join cast to a String[]. – Aussie Craig Mar 08 '09 at 06:17
  • 5
    You can use more clean solution with Concat. String.Concat(ba.Select(b => b.ToString("X2")) – Tomáš Linhart Aug 25 '10 at 17:03
  • 1
    @AussieCraig You only need the ToArray before .NET 4.0. at-Michael I think `string.Concat(from b in ba select b.ToString("X2"))` is even prettier - Any chance you could edit it in as a second line with the provisio that it leans on the .NET 4.0 `String.Concat(IEnumerable)` overload ? – Ruben Bartelink May 08 '13 at 11:44
42

Here is another method:

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();
}

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;
}

Alternatively, you could pre-build the translation table like so to achieve even faster results:

http://blogs.msdn.com/b/blambert/archive/2009/02/22/blambert-codesnip-fast-byte-array-to-hex-string-conversion.aspx

Nathan Moinvaziri
  • 5,506
  • 4
  • 29
  • 30
  • Using `StringBuilder Result = new StringBuilder(Bytes.Length * 2);` makes this slightly faster in my testing – Thymine Apr 09 '12 at 17:24
  • Can someone please explain the extra 0x00 between 0x09 and 0x0A in the HexValue array? Thanks in advance! – cyber-monk Dec 13 '13 at 17:43
  • 1
    The extra 0x00 is based on the distance from '9' to 'A'. If you run charmap on Windows (or whatever your platform's equivalent is), you'll see that there are 7 characters between. Hence, 7 0x00. – Joel Jul 09 '14 at 14:57
  • What if I wanted to return in byte array form the hex values? – Kush Aug 24 '20 at 12:24
24

I like using extension methods for conversions like this, even if they just wrap standard library methods. In the case of hexadecimal conversions, I use the following hand-tuned (i.e., fast) algorithms:

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;
}
kgriffs
  • 4,080
  • 5
  • 37
  • 42
22

Well I don't convert bytes to hex often so I have to say I don't know if there is a better way then this, but here is a way to do it.

StringBuilder sb = new StringBuilder();
foreach (byte b in myByteArray)
    sb.Append(b.ToString("X2"));

string hexString = sb.ToString();
Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
  • Looks about right. This really seems like something that should be in the framework, I swear people are always looking for a built in way to do this. Not sure why there isn't something already there. Oh well. – TJB Mar 08 '09 at 05:39
  • 1
    There is a built in way to do this, in the BitConverter class. – Guffa Mar 08 '09 at 06:59
  • 9
    Specify the capacity for the StringBuilder as myByteArray.Length*2 so that it doesn't have to reallocate during the loop. – Guffa Mar 08 '09 at 07:17
12

I thought I should provide an answer. From my test this method is the fastest

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();
    }
}
  • 1
    That's what you would use if you need to shave off that little extra in execution time, but consider that it comes to the price of added startup time and 5-6 kilobytes of data allocated. – Guffa Sep 02 '13 at 18:04
  • @Guffa: From memory it was significant because instead of appending one letter at a time it was doing 2. But in any case all solutions are fast but this one seemed to be noticeably faster then others. I'd give actual numbers but I don't remember them nor where I saved the test –  Sep 03 '13 at 06:58
12

Very fast extension methods (with reversal):

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;
    }
}

Beats all the others in the speed test above:

=== Long string test
BitConvertReplace calculation Time Elapsed 2415 ms
StringBuilder calculation Time Elapsed 5668 ms
LinqConcat calculation Time Elapsed 11826 ms
LinqJoin calculation Time Elapsed 9323 ms
LinqAgg calculation Time Elapsed 7444 ms
ToHexTable calculation Time Elapsed 1028 ms
ToHexAcidzombie calculation Time Elapsed 1035 ms
ToHexPatrick calculation Time Elapsed 814 ms
ToHexKurt calculation Time Elapsed 1604 ms
ByteArrayToHexString calculation Time Elapsed 1330 ms

=== Many string test
BitConvertReplace calculation Time Elapsed 2238 ms
StringBuilder calculation Time Elapsed 5393 ms
LinqConcat calculation Time Elapsed 9043 ms
LinqJoin calculation Time Elapsed 9131 ms
LinqAgg calculation Time Elapsed 7324 ms
ToHexTable calculation Time Elapsed 968 ms
ToHexAcidzombie calculation Time Elapsed 969 ms
ToHexPatrick calculation Time Elapsed 956 ms
ToHexKurt calculation Time Elapsed 1547 ms
ByteArrayToHexString calculation Time Elapsed 1277 ms

Community
  • 1
  • 1
Patrick
  • 1,766
  • 1
  • 15
  • 27
  • 2
    [d >> 4], [d & 0xf] is faster than [d / 0x10], [d % 0x10] – palota Nov 23 '15 at 17:18
  • Noted. It could also be improved with default parameter values rather than overloads. I don't have time to re-run the speed test right now though unfortunately. – Patrick Nov 25 '15 at 14:34
  • The prefix parameter to ToHex just causes that string to be included before the result, correct? I'm curious why, it seems just as easy for the caller to do the concatenation. Maybe its to save an alloc? – Frank Schwieterman Mar 28 '16 at 21:37
  • Could this be sped up even further by the use of the new Span ? – Adam Knights Sep 27 '18 at 14:39
7

Just to add one more answer to the pile, there is a System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary class that I've used which can convert bytes to and from hex:

string hex = new SoapHexBinary(bytes).ToString();
byte[] bytes = SoapHexBinary.Parse(hex).Value;

Not sure how it compares (benchmark) to other implementations, but IMO it is pretty simple -- especially for converting from hex back into bytes.

Allan
  • 1,063
  • 10
  • 18
5

With:

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

Barn Monkey
  • 245
  • 3
  • 11
4

You have to know the encoding of the string represented in bytes, but you can say System.Text.UTF8Encoding.GetString(bytes) or System.Text.ASCIIEncoding.GetString(bytes). (I'm doing this from memory, so the API may not be exactly correct, but it's very close.)

For the answer to your second question, see this question.

Community
  • 1
  • 1
Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
3

Here is a extension method for byte array (byte[]), e.g.,

var b = new byte[] { 15, 22, 255, 84, 45, 65, 7, 28, 59, 10 };
Console.WriteLine(b.ToHexString());


public static class HexByteArrayExtensionMethods
{
    private const int AllocateThreshold = 256;
    private const string UpperHexChars = "0123456789ABCDEF";
    private const string LowerhexChars = "0123456789abcdef";
    private static string[] upperHexBytes;
    private static string[] lowerHexBytes;

    public static string ToHexString(this byte[] value)
    {
        return ToHexString(value, false);
    }

    public static string ToHexString(this byte[] value, bool upperCase)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        if (value.Length == 0)
        {
            return string.Empty;
        }

        if (upperCase)
        {
            if (upperHexBytes != null)
            {
                return ToHexStringFast(value, upperHexBytes);
            }

            if (value.Length > AllocateThreshold)
            {
                return ToHexStringFast(value, UpperHexBytes);
            }

            return ToHexStringSlow(value, UpperHexChars);
        }

        if (lowerHexBytes != null)
        {
            return ToHexStringFast(value, lowerHexBytes);
        }

        if (value.Length > AllocateThreshold)
        {
            return ToHexStringFast(value, LowerHexBytes);
        }

        return ToHexStringSlow(value, LowerhexChars);
    }

    private static string ToHexStringSlow(byte[] value, string hexChars)
    {
        var hex = new char[value.Length * 2];
        int j = 0;

        for (var i = 0; i < value.Length; i++)
        {
            var b = value[i];
            hex[j++] = hexChars[b >> 4];
            hex[j++] = hexChars[b & 15];
        }

        return new string(hex);
    }

    private static string ToHexStringFast(byte[] value, string[] hexBytes)
    {
        var hex = new char[value.Length * 2];
        int j = 0;

        for (var i = 0; i < value.Length; i++)
        {
            var s = hexBytes[value[i]];
            hex[j++] = s[0];
            hex[j++] = s[1];
        }

        return new string(hex);
    }

    private static string[] UpperHexBytes
    {
        get
        {
            return (upperHexBytes ?? (upperHexBytes = new[] {
                "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
                "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
                "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
                "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
                "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
                "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
                "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
                "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
                "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
                "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
                "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
                "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
                "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
                "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
                "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
                "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" }));
        }
    }

    private static string[] LowerHexBytes
    {
        get
        {
            return (lowerHexBytes ?? (lowerHexBytes = new[] {
                "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
                "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
                "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
                "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
                "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
                "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
                "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
                "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
                "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
                "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
                "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
                "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
                "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
                "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
                "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
                "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" }));
        }
    }
}
markn
  • 279
  • 2
  • 3
  • 1
    Definitely a trade-off between performance and readability here, but looking up two characters at once is a clever optimization. Not sure why at least two people felt the need to downvote this -- if your goal is readability, this isn't the answer, but if you need to compute megabytes of hex strings per second, it definitely is! – Jonathan Gilbert Jan 13 '17 at 16:24
2

No one here mentioned the reason why you get the "System.Byte[]" string instead of the value, so I will.

When an object is implicitly cast to a String, the program will default to the object's public String ToString() method which is inherited from System.Object:

public virtual string ToString()
{
    return this.GetType().ToString();
}

If you find that you are often making this conversion, you could simply create a wrapper class and override this method like so:

public override string ToString()
{
    // do the processing here
    // return the nicely formatted string
}

Now each time you print this wrapper object you will get your value instead of the value from this.GetType().ToString().

Ozzy
  • 8,244
  • 7
  • 55
  • 95
  • I should have said "System.Byte[] ToString(string) doesn't seem to give me a hex string". I forgotten ToString is virtual. This is such an old question –  Sep 02 '13 at 13:36
2

You combine LINQ with string methods:

string hex = string.Join("",
  bin.Select(
    bin => bin.ToString("X2")
      ).ToArray());
mloskot
  • 37,086
  • 11
  • 109
  • 136
1

I think I made a faster byte array to string convertor:

public static class HexTable
{
    private static readonly string[] table = BitConverter.ToString(Enumerable.Range(0, 256).Select(x => (byte)x).ToArray()).Split('-');

    public static string ToHexTable(byte[] value)
    {
        StringBuilder sb = new StringBuilder(2 * value.Length);

        for (int i = 0; i < value.Length; i++)
            sb.Append(table[value[i]]);

        return sb.ToString();
    }

And the test set up:

static void Main(string[] args)
{
        const int TEST_COUNT = 10000;
        const int BUFFER_LENGTH = 100000;

        Random random = new Random();

        Stopwatch sw = new Stopwatch();
        Stopwatch sw2 = new Stopwatch();

        byte[] buffer = new byte[BUFFER_LENGTH];
        random.NextBytes(buffer);

        sw.Start();
        for (int j = 0; j < TEST_COUNT; j++)
            HexTable.ToHexTable(buffer);

        sw.Stop();

        sw2.Start();
        for (int j = 0; j < TEST_COUNT; j++)
            ToHexChar.ToHex(buffer);

        sw2.Stop();

        Console.WriteLine("Hex Table Elapsed Milliseconds: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine("ToHex Elapsed Milliseconds: {0}", sw2.ElapsedMilliseconds);
    }

The ToHexChar.ToHEx() method is the ToHex() method shown previously.

Results are as follows:

HexTable = 11808 ms ToHEx = 12168ms

It may not look that much of a difference, but it's still faster :)

Kris Dimitrov
  • 460
  • 4
  • 9
1

I'm not sure if you need perfomance for doing this, but here is the fastest method to convert byte[] to hex string that I can think of :

static readonly char[] hexchar = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static string HexStr(byte[] data, int offset, int len, bool space = false)
{
    int i = 0, k = 2;
    if (space) k++;
    var c = new char[len * k];
    while (i < len)
    {
        byte d = data[offset + i];
        c[i * k] = hexchar[d / 0x10];
        c[i * k + 1] = hexchar[d % 0x10];
        if (space && i < len - 1) c[i * k + 2] = ' ';
        i++;
    }
    return new string(c, 0, c.Length);
}
Toan
  • 321
  • 3
  • 6
1

As others have said it depends on the encoding of the values in the byte array. Despite this you need to be very careful with this sort of thing or you may try to convert bytes that are not handled by the chosen encoding.

Jon Skeet has a good article about encoding and unicode in .NET. Recommended reading.

Ash
  • 60,973
  • 31
  • 151
  • 169
0
private static string GuidToRaw(Guid guid)
{
    byte[] bytes = guid.ToByteArray();

    int сharCount = bytes.Length * 2;
    char[] chars = new char[сharCount];

    int index = 0;
    for (int i = 0; i < сharCount; i += 2)
    {
        byte b = bytes[index++];
        chars[i] = GetHexValue((int)(b / 16));
        chars[i + 1] = GetHexValue((int)(b % 16));
    }
    return new string(chars, 0, chars.Length);
}

private static char GetHexValue(int i)
{
    return (char)(i < 10 ? i + 48 : i + 55);
}
alex1kirch
  • 45
  • 5
0

Nice way to do this with LINQ...

var data = new byte[] { 1, 2, 4, 8, 16, 32 }; 
var hexString = data.Aggregate(new StringBuilder(), 
                               (sb,v)=>sb.Append(v.ToString("X2"))
                              ).ToString();
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69