我有一个班级
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];
};
code c ++:
afl = fopen(path, "rb");
DataqFmt dataqstr;
fread ((char*) &dataqstr, sizeof (dataqstr), 1, afl);
如何将此代码c ++转换为c#。请帮帮我
答案 0 :(得分:1)
我将假设编写此文件的C ++应用程序是为Win32 x86编译的,并在Win32 x86上运行。这允许我对DataqFmt
的排列方式做出假设。如果这些假设错误,您可以使用StructLayout
及相关属性的各种选项进行调整,例如MarshalAs
和FieldOffset
。
首先,我们将使用与DataqFmt
具有相同布局的C#结构:
[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.
另一种选择 - 如果你正在使用一个记录良好的平台无关的协议,这通常是一个很好的解决方案,因为你经常需要执行endian次转换 - 就是编写一个反序列化使用Stream
在BinaryReader
上运行的例程。我会留下这个练习给你。
答案 1 :(得分:0)
如果您正在使用visual studio,您还可以导入C ++代码并将其编译到CLR库中,以供任何其他.net程序集引用。如果您需要C ++代码快速运行,这尤其是一个好主意。
答案 2 :(得分: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()?”我回答说,“Endianess,数据类型大小,结构填充和对象布局的变化是足够的理由,不能在性能的幌子下继续传播相同的脆弱代码。任何体面的缓冲流无论如何都只会进行一次实际读取阅读工作由一个公正的机器人完成。除了I / O的成本,无论是否缓冲,都会使反射和字符串迭代的成本相形见绌。