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?
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?
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;
}
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)
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
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"))
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();
}
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)
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("-", "");
}
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.
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;
}
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();
}
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.
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;
}
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);
}
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.
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("-", "")