views:

44745

answers:

15

This is probably a common question over the Internet, but I couldn't find an answer that neatly explains how you can convert a byte array to a hexadecimal string, and vice versa.

Any takers?

+54  A: 

Either:

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)
{
  string hex = BitConverter.ToString(ba);
  return hex.Replace("-","");
}

There are even more variants of doing it, for example here.

The reverse conversion would go like this:

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;
}
Tomalak
missing a semicolon on hex.AppendFormat("{0:x2}", b) line
ee
You're using SubString. Doesn't this loop allocate a horrible amount of string objects?
Wim Coenen
Honestly - until it tears down performance dramatically, I would tend to ignore this and trust the Runtime and the GC to take care of it.
Tomalak
FWIW I could get a 4x speed-up on my machine by eliminating sub-string. Can't post the code because I wrote this for my employer.
Wim Coenen
Your StringToByteArray() fails if you have an odd number of hex characters. This is easily fixed by padding odd strings with a "0" at the front.
Carlos Rendon
Because a byte is two nibbles, any hex string that validly represents a byte array must have an even character count. A 0 should not be added anywhere - to add one would be making an assumption about invalid data that is potentially dangerous. If anything, the StringToByteArray method should throw a FormatException if the hex string contains an odd number of characters.
David
The first example returns a different value then the second one. Can anyone explain why?
iHeartDucks
+2  A: 

And to steal Tomalak's thunder... EXTENSION METHODS :) [disclaimer: completely untested code, btw .. just thought i'd add a quick post]

public static ByteExtensions
{
    public static string ToHexString(this byte[] value)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
        {
            hex.AppendFormat("{0:x2}", b)
        }
        return hex.ToString()
    }
}

etc.. use either of his three solutions (with the last one being an extension method on a string)

Pure.Krome
+15  A: 

You can use BitConverter.ToString Method:

byte[ ] bytes = {0,   1,   2,   4,   8,  16,  32,  64, 128, 255 }
Console.WriteLine( BitConverter.ToString( bytes ) );

Output:

00-01-02-04-08-10-20-40-80-FF

More Info: http://msdn.microsoft.com/en-us/library/3a733s97.aspx

Baget
+19  A: 

If you want more flexibility than BitConverter, but don't want those clonky 90s-style explicit loops, then you can do:

String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2"))
Will Dean
Even shorter: String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2"))
Nestor
Just a note that maxc's nice technique does need .net 4.0
Will Dean
+9  A: 

Since littering this among a half-dozen comments wouldn't be as easy to scan, I am going to make it an answer. That said, it doesn't really deserve any votes, but it is still pretty useful knowledge pertaining to the question, and it definitely looks better with the code formatted as such.

I ran each of the given "ToString" methods through some crude StopWatch performance testing and there is definitely a clear winner between BitConverter, StringBuilder appending as hex, and Array.ConvertAll. Every time I ran this, BitConverter was the fastest (even with the final string.Replace to remove hyphens).

On a random sentence of n=46, it was a ratio of 1:2:3 for BitConverter:Array.ConvertAll:StringBuilder. On a random text taken from Project Gutenberg of n=128010, it was much more dramatic (1:3:5). All tests were repeated 1000 times and averaged from there.

EDIT (2010-01-13) { The byte-manipulation from the MSDN forums that Waleed posted is, indeed, quite fast compared to the rest (and would seem more efficient with memory). With a new text file from Project Gutenburg, n=1573044, the results show the byte-manipulation to be almost 2.5 times faster than the BitConverter method, 11.5 times faster than Array.ConvertAll, and 13 times faster than StringBuilder.

I completely updated the testing code to make it [slightly] less painful to read.

I also updated the ByteArrayToHexStringViaStringBuilder function to make its output exactly match the rest by changing the AppendFormat to uppercase: "{0:X2}"). When I added the Assert to check they were all coming up with the same answer, that method blew up. }

In case anyone wants to play with my testing code, I have included it; just slap it into a new console app. It isn't pretty (or very easily extended to other tasks, but it still gives a pretty good idea which method is faster.

    static void Main(string[] args) {            
        byte[] TestSubject = GetSource("SomeRandomFile.txt");
        //byte[] TestSubject = System.Text.ASCIIEncoding.ASCII.GetBytes("put any sample string you want to test here and uncomment this line");
        string TypicalAnswer = ByteArrayToHexViaByteManipulation(TestSubject);
        int Iterations = 15;
        double AverageRunTicks;
        Func<byte[], string> Method;
        string MethodDescription;

        Method = ByteArrayToHexStringViaStringBuilder;
        MethodDescription = "StringBuilderToStringFromBytes";
        AverageRunTicks = GetAverageRun(Method, TestSubject, TypicalAnswer, Iterations);
        Console.WriteLine(string.Format("{0}: {1}", MethodDescription, AverageRunTicks));

        Method = ByteArrayToHexStringViaBitConverter;
        MethodDescription = "BitConverterToStringFromBytes";
        AverageRunTicks = GetAverageRun(Method, TestSubject, TypicalAnswer, Iterations);
        Console.WriteLine(string.Format("{0}: {1}", MethodDescription, AverageRunTicks));

        Method = ByteArrayToHexStringViaArrayConvertAll;
        MethodDescription = "ArrayConvertAllToStringFromBytes";
        AverageRunTicks = GetAverageRun(Method, TestSubject, TypicalAnswer, Iterations);
        Console.WriteLine(string.Format("{0}: {1}", MethodDescription, AverageRunTicks));

        Method = ByteArrayToHexViaByteManipulation;
        MethodDescription = "ByteManipulationToCharArray";
        AverageRunTicks = GetAverageRun(Method, TestSubject, TypicalAnswer, Iterations);
        Console.WriteLine(string.Format("{0}: {1}", MethodDescription, AverageRunTicks));

        Console.Read();
    }

    static string ByteArrayToHexStringViaArrayConvertAll(byte[] ba) {
        return string.Join(string.Empty, Array.ConvertAll(ba, x => x.ToString("X2")));
    }
    static string ByteArrayToHexStringViaBitConverter(byte[] ba) {
        string hex = BitConverter.ToString(ba);
        return hex.Replace("-", "");
    }
    static string ByteArrayToHexStringViaStringBuilder(byte[] ba) {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:X2}", b);
        return hex.ToString();
    }
    static string ByteArrayToHexViaByteManipulation(byte[] ba) {
        char[] c = new char[ba.Length * 2];
        byte b;
        for (int i = 0; i < ba.Length; i++) {
            b = ((byte)(ba[i] >> 4));
            c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
            b = ((byte)(ba[i] & 0xF));
            c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
        }
        return new string(c);
    }

    static byte[] GetSource(string fileName) {
        FileInfo TestSubjectFile = new System.IO.FileInfo(fileName);
        string TestFileContents = null;
        using (FileStream TestSubjectStream = TestSubjectFile.OpenRead()) {
            using (StreamReader TestSubjectReader = new StreamReader(TestSubjectStream)) {
                TestFileContents = TestSubjectReader.ReadToEnd();
            }
        }
        return System.Text.ASCIIEncoding.ASCII.GetBytes(TestFileContents);
    }

    static double GetAverageRun(Func<byte[], string> operation, byte[] victim, string expectedOutput, int iterations) {
        string TestResult = operation(victim); // also primes anything that may be a one-time cost
        Debug.Assert(expectedOutput == TestResult);

        List<long> ElapsedTicksCollection = new List<long>();
        Stopwatch Timer;
        for (int i = 1; i <= iterations; i++) {
            Timer = Stopwatch.StartNew();
            operation(victim);
            Timer.Stop();
            ElapsedTicksCollection.Add(Timer.ElapsedTicks);
        }
        return ElapsedTicksCollection.Average();
    }
patridge
my eyes.............!!!!!!!!!!!!!!!!!
Pure.Krome
Would you care to test the code from Waleed's answer? It seems to be very fast.http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa-in-c/632920#632920
Cristi Diaconescu
Despite making the code available for you to do the very thing you requested on your own, I updated the testing code to include Waleed answer. All grumpiness aside, it is much faster.
patridge
I got a different result when I used "ByteArrayToHexStringViaBitConverter" and "ByteArrayToHexStringViaStringBuilder". The latter one turned out to be "right". Is there any reason why the result from the two functions should be different?
iHeartDucks
+14  A: 

I just encountered the very same problem today and I came across this code:

private static string ByteArrayToHex(byte[] barray)
{
    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 + 0x37 : b + 0x30);
        b = ((byte)(barray[i] & 0xF));
        c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
    }

    return new string(c);
}

Source: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/3928b8cb-3703-4672-8ccd-33718148d1e3/ (see the post by PZahra) I modified the code a little to remove the 0x prefix

I did some performance testing to the code and it was almost 8 times faster than using BitConverter.ToString() (the fastest according to patridge's post)

Waleed Eissa
not to mention that this uses the least memory. No intermediate strings created whatsoever.
Chochos
+2  A: 

And for inserting into an SQL string (if you're not using command parameters):

public static String ByteArrayToSQLHexString(byte[] Source)
{
    return = "0x" + BitConverter.ToString(Source).Replace("-", "");
}
Jack Straw
+1  A: 

if you want to get the "4x speed increase" reported by wcoenen, then if it's not obvious: replace "hex.Substring(i, 2)" with "hex[i]+hex[i+1]"

you could also take it a step further and get rid of the i+=2 by using i++ in both places.

Olipro
+3  A: 

This is a great post. I like Waleed's solution. I haven't run it through patridge's test but it seems to be quite fast. I also needed the reverse process, converting a hex string to a byte array, so I wrote it as a reversal of Waleed's solution. Not sure if it's any faster than Tomalak's original solution. Again, I did not run the reverse process through patridge's test either.

private byte[] HexStringToByteArray(string hexString)
{
    int hexStringLength = hexString.Length;
    byte[] b = new byte[hexStringLength / 2];
    for (int i = 0; i < hexStringLength; i += 2)
    {
        int topChar = (hexString[i] > 0x40 ? hexString[i] - 0x37 : hexString[i] - 0x30) << 4;
        int bottomChar = hexString[i + 1] > 0x40 ? hexString[i + 1] - 0x37 : hexString[i + 1] - 0x30;
        b[i / 2] = Convert.ToByte(topChar + bottomChar);
    }
    return b;
}
Chris F
This code assumes the hex string uses upper case alpha chars, and blows up if the hex string uses lower case alpha. Might want to do a "uppercase" conversion on the input string to be safe.
Marc Novakowski
That's an astute observation Marc. The code was written to reverse Waleed's solution. The ToUpper call would slow down the algorithm some, but would allow it to handle lower case alpha chars.
Chris F
+4  A: 

There's a class called SoapHexBinary that does exactly what you want.

using System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary

public byte[] GetStringToBytes(string value)
{
    SoapHexBinary shb = SoapHexBinary.Parse(value);
    return shb.Value;
}

public string GetBytesToString(byte[] value)
{
    SoapHexBinary shb = new SoapHexBinary(value);
    return shb.ToString();
}
Mykroft
A: 

Never mind.

I'm unsure why two byte[] with the same contents aren't equal, but a quick BitConvert.ToString(hash) give me what I wanted.

tggagne
A: 

I did not get the code you suggested to work, Olipro. hex[i] + hex[i+1] apparently returned an int.

I did, however have some success by taking some hints from Waleeds code and hammering this together. It's ugly as hell but it seems to work and performs at 1/3 of the time compared to the others according to my tests (using patridges testing mechanism). Depending on input size. Switching around the ?:s to separate out 0-9 first would probably yield a slightly faster result since there are more numbers than letters.

public static byte[] StringToByteArray2(string hex)
{
    byte[] bytes = new byte[hex.Length/2];
    int bl = bytes.Length;
    for (int i = 0; i < bl; ++i)
    {
        bytes[i] = (byte)((hex[2 * i] > 'F' ? hex[2 * i] - 0x57 : hex[2 * i] > '9' ? hex[2 * i] - 0x37 : hex[2 * i] - 0x30) << 4);
        bytes[i] |= (byte)(hex[2 * i + 1] > 'F' ? hex[2 * i + 1] - 0x57 : hex[2 * i + 1] > '9' ? hex[2 * i + 1] - 0x37 : hex[2 * i + 1] - 0x30);
    }
    return bytes;
}
Fredrik Hu
+1  A: 

In terms of speed, this seems to be better than anything here:

  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);
  }
Alexey Borzenkov
+2  A: 

From Microsoft's developers, a nice, simple conversion:

public static string ByteArrayToString(byte[] ba) 
{
    // concat the bytes into one long string
    return ba.Aggregate(new StringBuilder(32),
                            (sb, b) => sb.Append(b.ToString("X2"))
                            ).ToString();
}

While the above is clean an compact, performance junkies will scream about it using enumerators. You can get peak performance with an improved version of Tomolak's original answer:

public static string ByteArrayToString(byte[] ba)   
{   
   StringBuilder hex = new StringBuilder(ba.Length * 2);   

   for(int i=0; i < ga.Length; i++)       // <-- use for loop is faster than foreach   
       hex.Append(ba[i].ToString("X2"));   // <-- ToString is faster than AppendFormat   

   return hex.ToString();   
} 

This is the fastest of all the routines I've seen posted here so far. Don't just take my word for it... performance test each routine and inspect it's IL code for yourself.

Mark
+2  A: 

Why make it complex. This is simple in visual studio.net 2008:

C#:

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

VB:

Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-", "")
Craig Poulton