Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#,从二进制文件读取结构_C#_File_Binary_Structure - Fatal编程技术网

C#,从二进制文件读取结构

C#,从二进制文件读取结构,c#,file,binary,structure,C#,File,Binary,Structure,我想从二进制文件中读取结构。 在C++中,我会这样做: stream.read((char*)&someStruct, sizeof(someStruct)); C#中有类似的方式吗?BinaryReader仅适用于内置类型。在.NET4中有一个MemoryMappedViewAccessor。它提供了像Read这样的方法,这似乎是我想要的,只是我必须手动跟踪我想要读取的文件中的位置。 有更好的方法吗?在C#中没有类似的方法。此外,由于其不可移植性,这是不推荐的序列化方式。改为使用。在

我想从二进制文件中读取结构。 在C++中,我会这样做:

stream.read((char*)&someStruct, sizeof(someStruct));
C#中有类似的方式吗?BinaryReader仅适用于内置类型。在.NET4中有一个MemoryMappedViewAccessor。它提供了像
Read
这样的方法,这似乎是我想要的,只是我必须手动跟踪我想要读取的文件中的位置。
有更好的方法吗?

在C#中没有类似的方法。此外,由于其不可移植性,这是不推荐的序列化方式。改为使用。

在C#中也可以执行类似的操作,但是您必须对结构应用许多属性,以便精确控制它在内存中的布局。默认情况下,JIT编译器控制结构成员在内存中的布局方式,这通常意味着在考虑速度和内存使用情况的情况下,对结构成员进行重新排列和填充,以获得最有效的布局

最简单的方法通常是使用BinaryReader读取文件中结构的单独成员,并将值放入类的属性中,即手动将数据反序列化到类实例中

通常,读取文件是此操作的瓶颈,因此读取单独成员的小开销不会显著影响性能。

公共静态类StreamExtensions
public static class StreamExtensions
{
    public static T ReadStruct<T>(this Stream stream) where T : struct
    {
        var sz = Marshal.SizeOf(typeof(T));
        var buffer = new byte[sz];
        stream.Read(buffer, 0, sz);
        var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        var structure = (T) Marshal.PtrToStructure(
            pinnedBuffer.AddrOfPinnedObject(), typeof(T));
        pinnedBuffer.Free();
        return structure;
    }
}
{ 公共静态T ReadStruct(此流),其中T:struct { var sz=Marshal.SizeOf(typeof(T)); var buffer=新字节[sz]; 读取(缓冲区,0,sz); var pinnedBuffer=GCHandle.Alloc(buffer,GCHandleType.pinted); var structure=(T)Marshal.PtrToStructure( AddrOfPinnedObject(),typeof(T)); pinnedBuffer.Free(); 回报结构; } }
您需要确保使用[StructLayout]和[FieldOffset]注释声明结构,以匹配文件中的二进制布局

编辑:

用法:

SomeStruct s = stream.ReadStruct<SomeStruct>();
SomeStruct s=stream.ReadStruct();

为了详细说明Guffa和jesperll的答案,这里有一个关于使用基本相同的
ReadStruct
方法(不作为扩展方法)读取ASF(WMV/WMA)文件头的示例

MemoryStream ms=新的MemoryStream(headerData);
AsfFileHeader AsfFileHeader=ReadStruct(ms);
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)]
内部结构AsfFileHeader
{
[Marshallas(UnmanagedType.ByValArray,SizeConst=16)]
公共字节[]对象标识;
公共UInt64对象大小;
公共UInt32头\对象\计数;
公共字节r1;
公共字节r2;
}

这里是Jesper代码的一个稍加修改的版本:

public static T? ReadStructure<T>(this Stream stream) where T : struct
{
    if (stream == null)
        return null;

    int size = Marshal.SizeOf(typeof(T));
    byte[] bytes = new byte[size];
    if (stream.Read(bytes, 0, size) != size) // can't build this structure!
        return null;

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
}
publicstatict?ReadStructure(此流),其中T:struct
{
if(流==null)
返回null;
int size=Marshal.SizeOf(typeof(T));
字节[]字节=新字节[大小];
if(stream.Read(bytes,0,size)!=size)//无法生成此结构!
返回null;
GCHandle=GCHandle.Alloc(字节,GCHandleType.pinted);
尝试
{
返回(T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(T));
}
最后
{
handle.Free();
}
}

它成功地处理EOF案例,因为它返回一个可为null的类型。

这取决于它们是如何处理的written@jesperll:这真是个坏主意,尤其是如果结构不是平坦的话。如果结构中的任何位置都有指针,那么引用的结构/类将不会写入输出。更糟糕的是,当读回时,它将指向无效的内存空间。True。如果你的结构中有数组之类的东西,你会遇到问题,因为它们不是值类型,但是如果你用它来解析文件格式,其中大部分是带有简单类型的头块,那么它是完全可行的+1这是完全可行的,我使用几乎相同的代码从ASF文件中读取二进制结构-@casperOne我不认为问题是要求复杂的对象序列化/反序列化机制非常酷
Marshal.PtrToStructure()
抛出枚举类型(可能是因为无法在枚举上使用
[StructLayout]
)。对于枚举,您可以使用
typeof(T).GetEnumUnderlyingType()
,它可以工作。在哪些情况下它不可移植?我用C++代码读取x86和x64中的相同数据,看起来很好。如果你把数据写入一个平台(例如x86),然后再读另一个(64),那么你可能会遇到问题。这正是我不理解的,因为我正在做。你可能有一个更详细地解释这个问题的链接吗?没有,对不起,我没有一个全面解释的链接。但我知道内存布局在不同的平台上是不同的,甚至在使用不同的编译器参数时也是如此。没有“内存中的字段必须以与源代码中相同的顺序出现”或“所有平台上的long由32位表示”或“字段在所有位置的32位数据包中对齐”的确切标准,因此不可能有这样的标准。在X86和X64中成功使用C++代码意味着你只是幸运而已。尝试使用编译器键或尝试为ARM编译。它更倾向于编译器实现,而不是x86 vs x64。听起来很合理。性能不是这里的主要问题,我只是觉得有点不方便。再想一想,我不想使用循环,只想读取一个数组。@B_old:编写几行代码一次读取一个值要容易得多,而不是为一个结构的所有成员获得正确的属性,这样就可以保证它在文件排列时准确地在内存中排列。无论您选择何种解决方案,都无法避免以某种形式使用循环。
public static T? ReadStructure<T>(this Stream stream) where T : struct
{
    if (stream == null)
        return null;

    int size = Marshal.SizeOf(typeof(T));
    byte[] bytes = new byte[size];
    if (stream.Read(bytes, 0, size) != size) // can't build this structure!
        return null;

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
}