将 sbyte[] 固定为字符串 C#

Posted

技术标签:

【中文标题】将 sbyte[] 固定为字符串 C#【英文标题】:Fixed sbyte[] to string C# 【发布时间】:2018-10-28 13:49:19 【问题描述】:

我正在尝试使用 StrucLayoutFieldOffset 从内存中获取字符串

但是我很难理解byte 的工作原理。

这是我的 ATM 代码:

[StructLayout(LayoutKind.Explicit)]
public unsafe struct InfoDetails

    [FieldOffset(0x14)]
    public fixed sbyte Name[50];

    public string getName
    
        get
        
            fixed (sbyte* namePtr = Name)
            
                return new string(namePtr);
            
        
    

此代码returnsT。预期结果是TEZ

关于我为什么做错的任何建议? 谢谢

【问题讨论】:

你能显示内存布局吗? 你为什么使用sbyte而不是未签名的byte?并将byte[] 转换为字符串使用Encoding 类。 也许 Name 是 unicode(所以两个字节/字符),而 string(sbyte*) 需要一个字节/字符。 好的,你知道字符串是如何存储的吗?据我了解,它们由 2 字节值(char)存储。 new string() 有 8 个重载:其中一个是指向以 0x00 结尾的字节数组的指针。所以你只得到第一个字节 试试char 【参考方案1】:

您可以将签名更改为:

[FieldOffset(0x14)]
public fixed char Name[25];

public string getName

    get
    
        fixed (char* namePtr = Name)
        
            return new string(namePtr);
        
    

注意我如何将sbyte 更改为char 并将缓冲区大小减半(因为sizeof(char) == 2)

或者您甚至可以更简单地向char* 添加一个演员表:

fixed (sbyte* namePtr = Name)

    return new string((char*)namePtr);

没有改变任何其他东西。

【讨论】:

解决方案一是给出“T”。但是解决方案 2 运行良好,而且看起来好多了!谢谢!【参考方案2】:

您的字符串编码似乎有问题。考虑以下测试代码:

unsafe

    InfoDetails d;
    var encoding = Encoding.Unicode;
    var stringBytes = encoding.GetBytes("TEZ");
    for(int i=0; i<stringBytes.Length; i++) d.Name[i] = (sbyte)stringBytes[i];
    Console.WriteLine(d.getName);

您确实会得到“T”,但如果将编码更改为Encoding.ASCII,您会得到预期的“TEZ”。

解决方法:你需要事先知道信息的编码,并据此生成字符串。看起来是 Unicode,所以先试试这个:

fixed (sbyte* namePtr = Name)

  return new string(namePtr, 0, 50, Encoding.Unicode);

【讨论】:

嘿,谢谢你的回答,它比我以前的要好得多,但它让我明白了:“TEZ\0\0\0ꃀб\0\0\0\0\0\0\ u0002\026\0\0\0\0䕀⚣\0" 我已经发布了一个现在可以使用的解决方案,我会在以后尝试改进它! 这很奇怪。你得到的确切字节数是多少? @Cesar 这意味着您的 Name[50] 声明并不正确。它假设字符串的大小固定为 50 字节,但在这种情况下,字符串要小得多,其余 50 字节甚至不是全零而是一些垃圾,这意味着它们可能属于完全不同的东西。【参考方案3】:

感谢大家的回答,他们帮助我找到了可行的解决方案。我真的不知道它是否是最好的:

[StructLayout(LayoutKind.Explicit)]
    public unsafe struct InfoDetails
    
        [FieldOffset(0x14)]
        public fixed byte Name[50];

        public string test
        
            get
            
                List<byte> clearBytes = new List<byte>();
                fixed (byte* namePtr = Name)
                
                    for (int i = 0; i < 50; i++)
                    
                        if (namePtr[i] == 0x0 && namePtr[i + 1] == 0x0)
                        
                            break;
                        
                        clearBytes.Add(namePtr[i]);
                    
                    if (clearBytes.Count() % 2 != 0)
                    
                        clearBytes.Add(0x00);
                    
                    return Encoding.Unicode.GetString(clearBytes.ToArray());
                
            
        

    

非常感谢!

【讨论】:

我认为您的解决方案过于复杂,而且手动处理字符串编码通常不是一个好主意。 new string(namePtr, 0, 50, Encoding.Unicode) 不是为你工作吗? @Konamiman 您完全正确,但您的解决方案导致了这一点:“TEZ\0\0\0ꃀб\0\0\0\0\0\0\u0002\026\ 0\0\0\0䕀⚣\0"

以上是关于将 sbyte[] 固定为字符串 C#的主要内容,如果未能解决你的问题,请参考以下文章

超过16位的字符串装16进制

从 C++ 库方法返回的“char*”获取 C# 字符串?

如何在 C# 中将固定字节/字符 [100] 转换为托管字符 []?

C# 连接字符串或字符数组

如何使用 C# 中使用 ref sbyte 的方法处理 COM 引用

在c#中如何实现判断一个数值是不是为整数(或小数)?