33

I have an array of bytes that I would like to store as a string. I can do this as follows:

byte[] array = new byte[] { 0x01, 0x02, 0x03, 0x04 };
string s = System.BitConverter.ToString(array);

// Result: s = "01-02-03-04"

So far so good. Does anyone know how I get this back to an array? There is no overload of BitConverter.GetBytes() that takes a string, and it seems like a nasty workaround to break the string into an array of strings and then convert each of them.

The array in question may be of variable length, probably about 20 bytes.

Darren Oster
  • 9,146
  • 10
  • 48
  • 66

7 Answers7

31

Not a built in method, but an implementation. (It could be done without the split though).

String[] arr=str.Split('-');
byte[] array=new byte[arr.Length];
for(int i=0; i<arr.Length; i++) array[i]=Convert.ToByte(arr[i],16);

Method without Split: (Makes many assumptions about string format)

int length=(s.Length+1)/3;
byte[] arr1=new byte[length];
for (int i = 0; i < length; i++)
    arr1[i] = Convert.ToByte(s.Substring(3 * i, 2), 16);

And one more method, without either split or substrings. You may get shot if you commit this to source control though. I take no responsibility for such health problems.

int length=(s.Length+1)/3;
byte[] arr1=new byte[length];
for (int i = 0; i < length; i++)
{
    char sixteen = s[3 * i];
    if (sixteen > '9') sixteen = (char)(sixteen - 'A' + 10);
    else sixteen -= '0';

    char ones = s[3 * i + 1];
    if (ones > '9') ones = (char)(ones - 'A' + 10);
    else ones -= '0';

    arr1[i] = (byte)(16*sixteen+ones);
}

(basically implementing base16 conversion on two chars)

CoderTao
  • 3,831
  • 1
  • 23
  • 18
29

You can parse the string yourself:

byte[] data = new byte[(s.Length + 1) / 3];
for (int i = 0; i < data.Length; i++) {
   data[i] = (byte)(
      "0123456789ABCDEF".IndexOf(s[i * 3]) * 16 +
      "0123456789ABCDEF".IndexOf(s[i * 3 + 1])
   );
}

The neatest solution though, I believe, is using extensions:

byte[] data = s.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • I gave you an up-vote for your LINQ solution, but got a compile-time error for for the "low-tech" solution. An extra set of parenthesis are needed, but its an easy fix. `(byte)("0123456789ABCDEF".IndexOf(s[i * 3]) * 16 + "0123456789ABCDEF".IndexOf(s[i * 3 + 1]))` – dana Mar 16 '12 at 01:16
  • @dana: Yes, you are right, it needs another parentheses. Good catch. – Guffa Mar 16 '12 at 01:24
  • Lambda/Extensions solution is very elegant! Thanks! – Derek Wade Jan 21 '21 at 09:13
28

If you don't need that specific format, try using Base64, like this:

var bytes = new byte[] { 0x12, 0x34, 0x56 };
var base64 = Convert.ToBase64String(bytes);
bytes = Convert.FromBase64String(base64);

Base64 will also be substantially shorter.

If you need to use that format, this obviously won't help.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
12
byte[] data = Array.ConvertAll<string, byte>(str.Split('-'), s => Convert.ToByte(s, 16));
vido
  • 121
  • 1
  • 2
  • 1
    Care to explain exactly what this code does? One-liners are nice, but there are usually many things happening in order to achieve the result required. – Sameer Singh Aug 06 '13 at 14:47
  • 2
    This is just a short version of CoderTao's answer. It replaces the byte array constructor and the "for" loop with "Array.ConvertAll" method. It uses a lambda expression to convert each HEX string to a byte. I was thinking it is self-explaining. – vido Aug 06 '13 at 16:19
1

I believe the following will solve this robustly.

public static byte[] HexStringToBytes(string s)
{
    const string HEX_CHARS = "0123456789ABCDEF";

    if (s.Length == 0)
        return new byte[0];

    if ((s.Length + 1) % 3 != 0)
        throw new FormatException();

    byte[] bytes = new byte[(s.Length + 1) / 3];

    int state = 0; // 0 = expect first digit, 1 = expect second digit, 2 = expect hyphen
    int currentByte = 0;
    int x;
    int value = 0;

    foreach (char c in s)
    {
        switch (state)
        {
            case 0:
                x = HEX_CHARS.IndexOf(Char.ToUpperInvariant(c));
                if (x == -1)
                    throw new FormatException();
                value = x << 4;
                state = 1;
                break;
            case 1:
                x = HEX_CHARS.IndexOf(Char.ToUpperInvariant(c));
                if (x == -1)
                    throw new FormatException();
                bytes[currentByte++] = (byte)(value + x);
                state = 2;
                break;
            case 2:
                if (c != '-')
                    throw new FormatException();
                state = 0;
                break;
        }
    }

    return bytes;
}
Jason Kresowaty
  • 16,105
  • 9
  • 57
  • 84
  • You're right, although since I am pretty sure the data will be in the right format (in either case an exception is thrown), I will probably go for the Split version or the Linq version - particularly as it saves about 40 lines of code. – Darren Oster Aug 04 '09 at 23:30
0

it seems like a nasty workaround to break the string into an array of strings and then convert each of them.

I don't think there's another way... the format produced by BitConverter.ToString is quite specific, so if there is no existing method to parse it back to a byte[], I guess you have to do it yourself

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
0

the ToString method is not really intended as a conversion, rather to provide a human-readable format for debugging, easy printout, etc.
I'd rethink about the byte[] - String - byte[] requirement and probably prefer SLaks' Base64 solution