How can I convert a hex string to a byte array? [duplicate]
Asked Answered
P

4

346

Can we convert a hex string to a byte array using a built-in function in C# or do I have to make a custom method for this?

Perspicacious answered 26/11, 2008 at 16:45 Comment(6)
You can easily convert string to byte[] in one line: var byteArray = Encoding.ASCII.GetBytes(string_with_your_data);Farnsworth
@mik-T, a hex string is in some format like 219098C10D7 which every two character converts to one single byte. your method is not usable.Chobot
This question does not seem to be duplicate of selected question. this one converts FROM hex string to byte array, however other question converts byte array to hex.Chobot
A simple one-liner with: BigInteger.Parse(str, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray()Dmso
@GregoryMorse Beware that BigInteger assumes the string represents a signed number, and will give unexpected result if the most significant bit of the MSB is 1, unless you do special treatment.Shyamal
Yes you should prepend a "00" to the string before parsing. You are right about this for sure, that code snippet will work only 50% of the time. "00" + strDmso
A
599

Here's a nice fun LINQ example.

public static byte[] StringToByteArray(string hex) {
    return Enumerable.Range(0, hex.Length)
                     .Where(x => x % 2 == 0)
                     .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                     .ToArray();
}
Ajay answered 26/11, 2008 at 16:56 Comment(18)
Good heavens!! Do you realize how INEFFICIENT that is??? Sure, it's fun, but LINQ is overused for things that should be done otherwise! LINQ code requires .NET 3.5 and requires referencing System.Core (which might otherwise not be needed). See the duplicate article for efficient solutions.Topsyturvydom
It's probably meant to be fun, not efficientFederico
This answer at least has the added benefit of being able to compile. keyAsBytes is undefined in the other one.Weaverbird
a bit more efficient and elegant IMO: Enumerable.Range(0, hex.Length/2) .Select(x => Byte.Parse(hex.Substring(2*x, 2), NumberStyles.HexNumber)) .ToArray(); Drop the where clause and use Byte.Parse()Wrier
Continually impressed with LINQ's elegance and versatilitySoricine
Came up with this to go the other direction. In case anyone else needs it... public static string ByteArrayToBinHex( this byte[] bytes ) { return bytes.Select( b => b.ToString( "X2" ) ).Aggregate( ( s1, s2 ) => s1 + s2 ); }Mentally
Byte array to hex string is simply BitConverter.ToString()Frank
The shorter version would be, Enumerable.Range(0, hex.Length / 2) .Select(x => Convert.ToByte(hex.Substring(x * 2, 2), 16)) .ToArray()Windblown
How is the performance of this?Tania
@Tania The performance of this code will probably match your test results ;)Haldis
This is the VB.net version of this answer, for those who need it: Public Shared Function StringToByteArray(hex As String) As Byte() Return Enumerable.Range(0, hex.Length).Where(Function(x) (x Mod 2) = 0).Select(Function(x) Convert.ToByte(hex.Substring(x, 2), 16)).ToArray() End FunctionStannic
Faster one-liner without substrings: Enumerable.Range(0, tx.Length / 2).Select((x) => { char c1 = tx[x * 2]; char c2 = tx[x * 2 + 1]; return (byte) (((c1 < 'A' ? c1 - '0' : c1 - 'A' + 10) << 4) + (c2 < 'A' ? c2 - '0' : c2 - 'A' + 10)); } ).ToArray()Dutyfree
even more fun public static string HexStrToBin(this string HexStr)Oops
@KevinP.Rice: As opposed to the "fast" version below, which is much harder to understand, doesn't compile correctly in the latest version of VS, and doesn't work with lower-case letters. It was copied+pasted into our codebase long ago, causing a serious and hard-to-find bug.Quitt
@BlueRaja-DannyPflughoeft The "fast" version below is in need of TLC. That doesn't mean a readable version of such an imperative approach does not exist. The "fast" version below contains the general structure, even if the implementation is lacking. Anyway.. really depends on how much said function is called in relationship to all other processing.Coralyn
Awesome!!!, i dont care that did "INEFFICIENT" , my string are very short. Thanks.Yearround
@Mentally you can use this instead Hex.ToHexString(byteArray); from the Org.BouncyCastle.Utilities.Encoders namespace.Palmetto
Try writing that in C++/CLI and see how far you get.Gordan
E
117

I did some research and found out that byte.Parse is even slower than Convert.ToByte. The fastest conversion I could come up with uses approximately 15 ticks per byte.

    public static byte[] StringToByteArrayFastest(string hex) {
        if (hex.Length % 2 == 1)
            throw new Exception("The binary key cannot have an odd number of digits");

        byte[] arr = new byte[hex.Length >> 1];

        for (int i = 0; i < hex.Length >> 1; ++i)
        {
            arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
        }

        return arr;
    }

    public static int GetHexVal(char hex) {
        int val = (int)hex;
        //For uppercase A-F letters:
        //return val - (val < 58 ? 48 : 55);
        //For lowercase a-f letters:
        //return val - (val < 58 ? 48 : 87);
        //Or the two combined, but a bit slower:
        return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
    }

// also works on .NET Micro Framework where (in SDK4.3) byte.Parse(string) only permits integer formats.

Equable answered 3/4, 2012 at 14:25 Comment(21)
It would be better if the GetHexVal function is inlined instead.Matthiew
I tried that, but somehow this is slightly faster. Maybe because the difference between the Heap and the Stack.Equable
Hmmm strange. Well I tested it with VB.NET 2.0 (2010 compiler) x86 using its if() ternary operator and it was definitely faster inlined. And in general, shouldn't IL operators be faster than any function calls?Matthiew
to answer that you would need to know a lot about how the compiler makes its decisions about automatic inliningEstes
This worked well for me but I think in most cases you will want the bytes in the array in the same order as the hex chars in the string. I added Array.Reverse(arr); before the return statement in StringToByteArrayFastest() to fix this.Fowl
The bytes are in same order as hex chars on my side. What do you mean by reverse?Professionalism
Might want to take a look at codeproject.com/Tips/447938/… It should be a lot faster to replace GetHexVal() with a tableDebbradebby
Note that this method doesn't use ANY bounds checking, so if you pass a non-hexadecimal string like "\b\b", you will get wrong results.Cimabue
why you shift hex.length to the right? hex.Length >> 1Galibi
@MoHradA Shifting right by 1 is a division by 2. One hex character represents 4 bits, you need 8 bits per byte, so 2 characters are converted into one byte.Medication
Would hex.Length & 1 be faster than hex.Length % 2?Rooks
I just found this code pasted into a program I have to maintain. It no longer compiles and throws a CS0307 (variable 'i' cannot be used with type args) and a CS0118 ('hex' is a variable but used as a type). Using the bitwise shifting (instead of a plain old "/ 2") may seem cool but this is a clear case of premature optimization evil for 99.99% of developers who comes to this question.Querida
@Querida the problem is not with the code it's with the compiler. According to MSDN order of operations the code is valid. Easy test is to open it in VS2013 (compiles) open it in VS2015+ and you'll see said bug. Wrap it in parenthesis and the compile error is goneSought
@RobertSnyder - my point isn't about the compilation (though it was more or less broken overnight with no recent changes to the build server). We had a consultant copy paste this code into a program that did not need this level of performance. .Querida
I don't think there even is a difference in performance. What's wrong with bit shifting?Newfangled
something is wrong. "cc"(204) encode as "ec" (236)Dennard
@vitidev: Take special note of the comments in GetHexVal. As written, this code is case-sensitive (which is one of the reasons it's faster).Dasyure
@Querida Sounds like a coding practice error. A copy and paste of any answer or source should be viewed the same, regardless of any perceived need for performance or lack of: the bugs, limitations, and quirks are independent and bountiful. That said, I would use a simple i / 2, and otherwise many minor changes to what is presented here (including using char literals like 'a')..Coralyn
works for me, copy past, no changesPrimine
So... I just benchmarked the division by 2 vs the bit shift by 1. The mean is that the division operator is .0011 nanoseconds faster than the bit shift operator with the division operator essentially at 0 it's so fast with .0011 nanoscends mean time for the bit shift operator: 4 / 2 is faster than 4 >> 1 according to that benchmark.Polymer
Up to .net 5 this was the fastest method but now you should use Convert.FromHexString and for the other direction Convert.ToHexString.Danielldaniella
C
65

The following code changes the hexadecimal string to a byte array by parsing the string byte-by-byte.

public static byte[] ConvertHexStringToByteArray(string hexString)
{
    if (hexString.Length % 2 != 0)
    {
        throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString));
    }

    byte[] data = new byte[hexString.Length / 2];
    for (int index = 0; index < data.Length; index++)
    {
        string byteValue = hexString.Substring(index * 2, 2);
        data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
    }

    return data; 
}
Caresa answered 22/11, 2011 at 23:47 Comment(3)
Should it not be "for (int index = 0; index < HexAsBytes.Length; index++)" ?Plexiglas
68016101061B4A60193390662046804020422044204000420040402060226024676DB16 with this i am getting The binary key cannot have an odd number of digitsChitter
@Chitter And did you look at your input and the message you're getting?Orchitis
A
13

I think this may work.

public static byte[] StrToByteArray(string str)
    {
        Dictionary<string, byte> hexindex = new Dictionary<string, byte>();
        for (int i = 0; i <= 255; i++)
            hexindex.Add(i.ToString("X2"), (byte)i);

        List<byte> hexres = new List<byte>();
        for (int i = 0; i < str.Length; i += 2)            
            hexres.Add(hexindex[str.Substring(i, 2)]);

        return hexres.ToArray();
    }
Aristotle answered 25/5, 2012 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.