字节数组到struct

时间:2015-06-13 10:30:22

标签: c# struct bytearray converter

我在转换字节数组的字符串部分时遇到了问题。

我的结构看起来像这样:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Message
{
    public int id;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string text;
}

创建测试字节数组:

private static byte[] CreateMessageByteArray()
{
    int id = 69;
    byte[] intBytes = BitConverter.GetBytes(id);

    string text = "test";
    byte[] stringBytes = GetBytes(text);

    IEnumerable<byte> rv = intBytes.Concat(stringBytes);

    return rv.ToArray();
}

将我的bytearray转换为结构的方法:

static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    var result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return result;
}

当我使用ByteArrayToStructure的结果调用CreateMessageByteArray()时,我得到一个id = 60且text =“t”的结构。

为什么我不能获得整个字符串,例如“test”?

编辑: 这是我忘记编码的代码:

    static byte[] GetBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }

3 个答案:

答案 0 :(得分:4)

问题出在这一行:

byte[] stringBytes = GetBytes(text);

如何将字符串转换为字节数组?您可能正在使用Unicode编码,它将每个字符存储为两个字节,并且因为您的字符串是ASCII集,所以每隔一个字节将为零:

byte[] stringBytes = new UnicodeEncoding().GetBytes(text);
// will give you { 't', '\0', 'e', '\0', 's', '\0', 't', '\0' }

这些零错误地将编组机制误认为它们是终端字符,因此字符串在't'之后结束。

相反,您可以使用ASCII编码(每个字符存储一个字节):

byte[] stringBytes = new ASCIIEncoding().GetBytes(text);
// will give you { 't', 'e', 's', 't' }
// but will lose non-ASCII character information

或者您可以使用UTF8编码(可变长度):

byte[] stringBytes = new UTF8Encoding().GetBytes(text);
// will give you { 't', 'e', 's', 't' }    
// and retain non-ASCII character information, but it's somewhat
// trickier to rebuild the string correctly in case of non-ASCII
// information present

答案 1 :(得分:4)

除了其他两个答案之外,如果您希望text字段中的字符串始终为Unicode,则可以在CharSet = CharSet.Unicode属性中包含[StructLayout]

答案 2 :(得分:1)

也许GetBytes方法并不像你期望的那样工作。 这个linqpad对我来说很好用:

void Main()
{
    var result = ByteArrayToStructure<Message>(CreateMessageByteArray());
    result.Dump();
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Message
{
    public int id;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string text;
}

private static byte[] CreateMessageByteArray()
{
    int id = 69;
    byte[] intBytes = BitConverter.GetBytes(id);

    string text = "test";
    byte[] stringBytes = Encoding.UTF8.GetBytes(text);

    IEnumerable<byte> rv = intBytes.Concat(stringBytes);

    return rv.ToArray();
}

static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    var result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return result;
}

输出:

id    69 
text  test