tags:

views:

1059

answers:

6

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.

+6  A: 

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
Yeah, it might come down to that. I was hoping there was a method I was missing somewhere - seems strange not to bake it in somewhere...
Darren Oster
"It could be done without the split though" : how ?
Thomas Levesque
Hmm... I think the Split version is neater and probably quicker (in one of those infinitesimal ways)
Darren Oster
The Split version isn't faster. Split has to allocate an array for its return, and if the string is large, it can be quite slow. For large strings, it would be faster to loop through the characters in the string.
SLaks
Fair call. Faster to type, though.. :)
Darren Oster
A: 

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
+4  A: 

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
Nice extension...
Darren Oster
+1  A: 

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;
}
binarycoder
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
+2  A: 

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
A: 

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

DataFiddler