340

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?

Rob
  • 26,989
  • 16
  • 82
  • 98
Blankman
  • 259,732
  • 324
  • 769
  • 1,199
  • 2
    You can easily convert string to byte[] in one line: var byteArray = Encoding.ASCII.GetBytes(string_with_your_data); – mikhail-t Jun 06 '13 at 22:02
  • 35
    @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. – AaA Dec 12 '14 at 07:52
  • 4
    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. – AaA Dec 12 '14 at 07:53
  • 2
    A simple one-liner with: `BigInteger.Parse(str, System.Globalization.NumberStyles.HexNumber).ToByteArray().Reverse().ToArray()` – Gregory Morse Mar 10 '20 at 21:21
  • @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. – Oskar Berggren Sep 11 '20 at 10:17
  • 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" + str``` – Gregory Morse Sep 12 '20 at 18:16

4 Answers4

588

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();
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 210
    **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. – Kevin P. Rice May 31 '11 at 07:58
  • 42
    It's probably meant to be fun, not efficient – Karsten Sep 05 '11 at 09:27
  • 2
    This answer at least has the added benefit of being able to compile. keyAsBytes is undefined in the other one. – Ronnie Overby Jan 28 '12 at 02:41
  • 5
    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()` – 3dGrabber Mar 14 '12 at 14:24
  • 32
    Continually impressed with LINQ's elegance and versatility – Michael Richardson Apr 10 '15 at 13:35
  • 6
    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 ); } – dviljoen Jun 15 '15 at 16:31
  • 1
    Byte array to hex string is simply BitConverter.ToString() – dlchambers Jul 20 '15 at 16:06
  • 49
    The shorter version would be, `Enumerable.Range(0, hex.Length / 2) .Select(x => Convert.ToByte(hex.Substring(x * 2, 2), 16)) .ToArray()` – Hossein Shahdoost Nov 10 '15 at 08:33
  • How is the performance of this? – AlbatrossCafe Apr 14 '17 at 01:11
  • @AlbatrossCafe The performance of this code will probably match your test results ;) – Gusdor Sep 21 '17 at 08:58
  • 1
    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 Function` – Fabio Jun 12 '18 at 10:45
  • 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() – Wolfgang Grinfeld Jun 26 '18 at 07:37
  • even more fun `public static string HexStrToBin(this string HexStr)` – Mat Jul 13 '18 at 09:37
  • 5
    @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. – BlueRaja - Danny Pflughoeft Dec 10 '18 at 11:35
  • @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. – user2864740 Jul 16 '20 at 00:02
  • Awesome!!!, i dont care that did "INEFFICIENT" , my string are very short. Thanks. – Fernando Vera Nov 02 '21 at 22:46
  • @dviljoen you can use this instead `Hex.ToHexString(byteArray);` from the `Org.BouncyCastle.Utilities.Encoders` namespace. – David Klempfner Feb 07 '22 at 09:37
  • Try writing that in C++/CLI and see how far you get. – Igor Levicki Aug 02 '23 at 19:52
112

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.

nikib3ro
  • 20,366
  • 24
  • 120
  • 181
CainKellye
  • 1,475
  • 1
  • 10
  • 10
  • It would be better if the GetHexVal function is inlined instead. – Fit Dev Apr 08 '12 at 13:09
  • 2
    I tried that, but somehow this is slightly faster. Maybe because the difference between the Heap and the Stack. – CainKellye Apr 23 '12 at 14:24
  • 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? – Fit Dev Apr 24 '12 at 13:40
  • 2
    to answer that you would need to know a lot about how the compiler makes its decisions about automatic inlining – John Nicholas May 17 '12 at 15:02
  • 1
    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. – Jason Wheeler Mar 09 '13 at 22:25
  • 2
    The bytes are in same order as hex chars on my side. What do you mean by reverse? – bytefire Sep 06 '13 at 06:59
  • Might want to take a look at http://www.codeproject.com/Tips/447938/High-performance-Csharp-byte-array-to-hex-string-t It should be a lot faster to replace GetHexVal() with a table – userx Nov 14 '13 at 23:33
  • 1
    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. – nikeee Jul 28 '15 at 02:16
  • 2
    why you shift hex.length to the right? `hex.Length >> 1` – Mohammad Hossein Amri Mar 23 '16 at 23:22
  • 2
    @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. – Vincent Van Den Berghe Jun 16 '16 at 11:10
  • Would `hex.Length & 1` be faster than `hex.Length % 2`? – Happypig375 Jan 14 '17 at 12:02
  • 9
    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. – StingyJack Jun 04 '17 at 12:49
  • 2
    @StingyJack 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 gone – Robert Snyder Oct 09 '17 at 13:23
  • 3
    @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. . – StingyJack Oct 09 '17 at 18:08
  • 1
    I don't think there even is a difference in performance. What's wrong with bit shifting? – recursive Oct 11 '17 at 03:28
  • **something is wrong**. "cc"(204) encode as "ec" (236) – vitidev Mar 30 '19 at 08:23
  • 1
    @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). – Brian Oct 08 '19 at 17:36
  • @StingyJack 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').. – user2864740 Jul 15 '20 at 23:57
  • 1
    works for me, copy past, no changes – Walter Verhoeven Jan 05 '21 at 14:38
  • 1
    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. – Paul Carlton Dec 15 '21 at 20:43
61

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; 
}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Aswath Krishnan
  • 1,131
  • 1
  • 9
  • 16
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();
    }
Community
  • 1
  • 1
Rick
  • 139
  • 1
  • 2