要构造的字节数组
Byte array to struct
我在转换字节数组的字符串部分时遇到问题。
我的结构如下所示:
[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;
}
当我用 CreateMessageByteArray()
的结果调用 ByteArrayToStructure
时,我得到一个 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;
}
可能 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
问题出在这一行:
byte[] stringBytes = GetBytes(text);
如何将字符串转换为字节数组?您可能正在使用 Unicode 编码,它将每个字符存储为两个字节,并且因为您的字符串在 ASCII 集中,所以每隔一个字节将为零:
byte[] stringBytes = new UnicodeEncoding().GetBytes(text);
// will give you { 't', '[=11=]', 'e', '[=11=]', 's', '[=11=]', 't', '[=11=]' }
这些零误导编组机制假设它们是终止字符,因此字符串在 '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
除了其他两个答案之外,如果您希望 text
字段中的字符串始终为 Unicode,您可以在 [StructLayout]
属性中包含 CharSet = CharSet.Unicode
我在转换字节数组的字符串部分时遇到问题。
我的结构如下所示:
[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;
}
当我用 CreateMessageByteArray()
的结果调用 ByteArrayToStructure
时,我得到一个 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;
}
可能 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
问题出在这一行:
byte[] stringBytes = GetBytes(text);
如何将字符串转换为字节数组?您可能正在使用 Unicode 编码,它将每个字符存储为两个字节,并且因为您的字符串在 ASCII 集中,所以每隔一个字节将为零:
byte[] stringBytes = new UnicodeEncoding().GetBytes(text);
// will give you { 't', '[=11=]', 'e', '[=11=]', 's', '[=11=]', 't', '[=11=]' }
这些零误导编组机制假设它们是终止字符,因此字符串在 '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
除了其他两个答案之外,如果您希望 text
字段中的字符串始终为 Unicode,您可以在 [StructLayout]
属性中包含 CharSet = CharSet.Unicode