我正在尝试了解如何从以下结构中的不安全字节指针获取字符串。 SDL_TEXTINPUTEVENT_TEXTSIZE是32。
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SDL_TextInputEvent
{
public SDL_EventType type;
public UInt32 timestamp;
public UInt32 windowID;
public fixed byte text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
}
我试过了:
byte[] rawBytes = new byte[SDL_TEXTINPUTEVENT_TEXT_SIZE];
unsafe
{
Marshal.Copy((IntPtr)rawEvent.text.text, rawBytes, 0, SDL_TEXTINPUTEVENT_TEXT_SIZE);
}
string text = System.Text.Encoding.UTF8.GetString(rawBytes);
哪种工作,但给了我一个字符串,其中包含了实际输入的字符之外的大量额外字节。我应该解析字节数组并搜索0终止字符以避免过多吗?
我完全误解了什么吗?
作为参考,正在编组到.NET运行时的原始C结构是:
typedef struct SDL_TextInputEvent
{
Uint32 type;
Uint32 timestamp;
Uint32 windowID;
char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
} SDL_TextInputEvent;
答案 0 :(得分:4)
您确实需要找到空终止符。并Marshal.Copy
不会这样做。如果您的文本是ANSI编码的,您可以使用Marshal.PtrToStringAnsi
。但是UTF-8没有这样的功能。因此,您需要迭代数组以查找零字节。当您遇到您知道缓冲区的实际长度时,您可以修改现有代码以使用该长度而不是最大可能长度。
答案 1 :(得分:0)
我刚遇到.NET Core的相同问题。幸运的是,自.NET Core 1.1 / .NET Standard 2.1开始,有一种方法Marshal.PtrToStringUTF8
,它提供了本机UTF-8字符串的转换。
给出此结构:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct NativeType
{
public int SomeNumber;
public unsafe fixed byte SomeString[16];
}
我们可以按以下方式将二进制数据解码为ASCII和UTF-8:
var byteArrayAscii = new byte[] { 0x78, 0x56, 0x34, 0x12, 0x41, 0x53, 0x43, 0x49, 0x49, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
var byteArrayUtf8 = new byte[] { 0xef, 0xcd, 0xab, 0x89, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x3a, 0x20, 0xf0, 0x9f, 0x91, 0x8d, 0x21, 0x00, 0x00, 0x00, 0x00 };
using var outputStream = File.OpenWrite("output.txt");
using var outputWriter = new StreamWriter(outputStream);
unsafe
{
var decoded1 = MemoryMarshal.Read<NativeType>(byteArrayAscii);
outputWriter.WriteLine($"Number 1: {decoded1.SomeNumber:x8}");
outputWriter.WriteLine($"String 1: {Marshal.PtrToStringAnsi(new IntPtr(decoded1.SomeString))}");
}
unsafe
{
var decoded2 = MemoryMarshal.Read<NativeType>(byteArrayUtf8);
outputWriter.WriteLine($"Number 2: {decoded2.SomeNumber:x8}");
outputWriter.WriteLine($"String 2: {Marshal.PtrToStringUTF8(new IntPtr(decoded2.SomeString))}");
}
输出:
Number 1: 12345678
String 1: ASCII!
Number 2: 89abcdef
String 2: Emoji: ?!
(包含“ thumbsup”表情符号,某些浏览器可能无法正确显示)
注意:
char
不适用于ASCII或UTF-8编码的数据,因为在C#char
always has a size of 16 bits(UTF-16)中:
固定大小的char缓冲区每个字符始终占用两个字节,而不管编码如何。即使使用
CharSet = CharSet.Auto
或CharSet = CharSet.Ansi
将char缓冲区编组到API方法或结构中,也是这样。