-1

我有一堂课

class DataqFmt 
{
public:
    unsigned short  SR_number;
    unsigned short  SR_numerator;
    unsigned char   offset;
    unsigned char   nbytes;
    short       hdr_bytes;
    unsigned long   dat_bytes;
        char        dummy[1144];
};

代码c++:

afl = fopen(path, "rb");
DataqFmt dataqstr;
fread ((char*) &dataqstr, sizeof (dataqstr), 1, afl);

如何将此代码 c++ 转换为 c# 。请帮我

4

3 回答 3

1

我将假设编写此文件的 C++ 应用程序是为 Win32 x86 编译并在 Win32 x86 上运行的。这使我可以对安排的方式做出假设DataqFmt。如果这些假设是错误的,您可以使用 的各种选项StructLayout和相关属性(例如MarshalAs和)来调整它们FieldOffset

首先,我们将使用具有相同布局的 C# 结构DataqFmt

[StructLayout(LayoutKind.Sequential)]
struct DataqFmtCs
{
    public ushort SR_number; // assuming the C++ type unsigned short is 2 bytes
    public ushort SR_numerator;
    public byte offset; // assuming the C++ type unsigned char was an unsigned and 1 byte
    public byte nbytes;
    public short hdr_bytes; // assuming the C++ type short was 2 bytes
    public uint dat_bytes; // assuming the C++ type unsigned long was 4 bytes
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1144)]
    public sbyte[] dummy; // assuming the C++ type char was signed and 1 byte
}

有了这个结构,我们现在需要进行文件 I/O 来获取原始字节。

int dataqfmtSize = Marshal.SizeOf(typeof(DataqFmtCs));
var buffer = new byte[dataqfmtSize];
using (var fs = File.OpenRead("path\\to\\the\\file")) {
    int bytesRead = fs.Read(buffer, 0, buffer.Length);
    if (bytesRead != buffer.Length) {
        // handle this
    }

    DataqFmtCs someThing = GetDataqFmtFromBuffer(buffer);
}

最后,我们有了转换例程GetDataqFmtFromBuffer

private static DataqFmtCs GetDataqFmtFromBuffer(byte[] buffer) {
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    try {
        return (DataFmtCs)Marshal.PtrToStructure(
            handle.AddrOfPinnedObject(),
            typeof(DataFmtCs));
    } finally {
        handle.Free();
    }
}

Marshal.PtrToStrucutre正在做绝大多数的转换工作。其余代码只是定义结构并执行基本的 .NET 文件 I/O。

另一种选择——如果你正在使用一个与平台无关的有据可查的协议,这通常是一个很好的解决方案,因为你经常需要执行字节序转换——是编写一个反序列化例程,该例程Stream使用BinaryReader. 我会把这个留给你做练习。

于 2013-11-25T06:52:59.603 回答
0

如果您使用的是 Visual Studio,您还可以导入 C++ 代码并将其编译到 CLR 库中,以供任何其他 .net 程序集引用。如果您需要 C++ 代码快速运行,这尤其是一个好主意。

于 2013-11-27T18:57:53.357 回答
0

如果您愿意承认 C++ fread() 本质上是脆弱且不可移植的,那么最好编写一个抽象层来读取(和写入)代码数据类型,例如这样的格式很有用:

// returns true on success, false on EOF
public static bool Read(Stream stm, out int val)
{ /* ... */ }

public static bool Read(Stream stm, out short val)
{ /* ... */ }

然后你可以写你的代码是这样的:

if (!Read(stm, out _someMember))
    throw SomeException(); // or return a fail code
if (!Read(stm, out _someOtherMember))
    throw SomeException();

或者在我的一些代码的情况下,我编写了一个方法,它给出了一个对象、一个流和一个字段或一个属性的名称,读取适当大小的值并设置成员(或者如果它可以抛出异常'找不到它),然后是一个类似的方法,它适用于一个对象和一组名称(或者实际上是可变数量的字符串参数,所以初始化看起来像这样:

    public static TTHorizontalHeader FromStream(Stream stm)
    {
        TTHorizontalHeader header = new TTHorizontalHeader();
        if (!Reader.ReadType(stm, header,
            "TableVersion", "Ascender", "Descender", "LineGap", "AdvanceWidthMax", "MinLeftSideBearing",
            "MinRightSideBearing", "XMaxExtent", "CaretSlopeRise", "CaretSlopeRun", "CaretOffset", "Reserved0",
            "Reserved1", "Reserved2", "Reserved3", "MetricDataFormat", "NumberOfHMetrics"))
            return null;
        return header;
    }

但你说,“C++ 不会运行得更快,因为它只执行一个 fread() 吗?” 我回答说,“字节序、数据类型大小、结构填充和对象布局的变化足以让我们不要以性能为幌子继续传播相同的脆弱代码。任何体面的缓冲流无论如何都只会进行一次实际读取,并且读取工作由公正的机器人完成,除了 I/O 的成本,无论是否缓冲,都会使反射和字符串迭代的成本相形见绌。

于 2013-11-27T19:15:38.050 回答