【问题标题】:Is there a BinaryReader in C++ to read data written from a BinaryWriter in C#?C++ 中是否有 BinaryReader 来读取从 C# 中的 BinaryWriter 写入的数据?
【发布时间】:2009-10-06 13:26:03
【问题描述】:

我已经用 C# 中的 BinaryWriter 将几个整数、char[] 等写入数据文件。使用 BinaryReader(在 C# 中)读回文件,我可以完美地重新创建文件的所有部分。

但是,尝试使用 C++ 重新读取它们会产生一些可怕的结果。我正在使用 fstream 尝试读回数据,但数据未正确读入。在 C++ 中,我使用 ios::in|ios::binary|ios::ate 设置了一个 fstream,并使用 seekg 定位我的位置。然后我读取了接下来的四个字节,它们被写为整数“16”(并正确读取到 C# 中)。这在 C++ 中读取为 1244780 (不是内存地址,我检查了)。为什么会这样? C++ 中是否有与 BinaryReader 等价的东西?我注意到它在 msdn 上提到过,但那是 Visual C++,而智能感知在我看来甚至不像 C++。

编写文件的示例代码(C#):

    public static void OpenFile(string filename)
    {
        fs = new FileStream(filename, FileMode.Create);
        w = new BinaryWriter(fs);

    }

    public static void WriteHeader()
    {
        w.Write('A');
        w.Write('B');
    }

    public static byte[] RawSerialize(object structure)
    {
        Int32 size = Marshal.SizeOf(structure);
        IntPtr buffer = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(structure, buffer, true);
        byte[] data = new byte[size];
        Marshal.Copy(buffer, data, 0, size);
        Marshal.FreeHGlobal(buffer);
        return data;
    }

    public static void WriteToFile(Structures.SomeData data)
    {
        byte[] buffer = Serializer.RawSerialize(data);
        w.Write(buffer);
    }

我不确定如何向您展示数据文件。

读回数据示例(C#):

        BinaryReader reader = new BinaryReader(new FileStream("C://chris.dat", FileMode.Open));
        char[] a = new char[2];
        a = reader.ReadChars(2);
        Int32 numberoffiles;
        numberoffiles = reader.ReadInt32();
        Console.Write("Reading: ");
        Console.WriteLine(a);
        Console.Write("NumberOfFiles: ");
        Console.WriteLine(numberoffiles);

这是我想用 C++ 来执行的。初始尝试(在第一个整数处失败):

 fstream fin("C://datafile.dat", ios::in|ios::binary|ios::ate);
 char *memblock = 0;
 int size;
 size = 0;
 if (fin.is_open())
 {
  size = static_cast<int>(fin.tellg());
  memblock = new char[static_cast<int>(size+1)];
  memset(memblock, 0, static_cast<int>(size + 1));

  fin.seekg(0, ios::beg);
  fin.read(memblock, size);
  fin.close();
  if(!strncmp("AB", memblock, 2)){ 
   printf("test. This works."); 
  }
  fin.seekg(2); //read the stream starting from after the second byte.
  int i;
  fin >> i;

编辑:似乎无论我在哪个位置使用“seekg”,我都会收到完全相同的值。

【问题讨论】:

  • 你能给我们展示一段代码(或整个代码)和一个二进制文件的例子吗?
  • 我已经发布了一些代码。不确定我可以将二进制文件上传到哪里。
  • 您在 C# 阅读器中阅读 chris.dat,在 C++ 阅读器中阅读 datafile.dat...
  • @Andy,名称差异只是我来回测试的结果。
  • 尝试只写一个 int 以避免担心字符大小。把它写出来,看看你能不能读出来,然后用十六进制编辑器报告文件的样子。

标签: c# c++ file-io binaryfiles binaryreader


【解决方案1】:

您意识到 C# 中的 char 是 16 位,而不是 C# 中通常的 8 位。这是因为 C# 中的 char 旨在处理 Unicode 文本而不是原始数据。因此,使用 BinaryWriter 写入字符将导致写入 Unicode 而不是原始字节。

这可能会导致您错误地计算整数的偏移量。我建议您在十六进制编辑器中查看该文件,如果您无法解决问题,请在此处发布文件和代码。

EDIT1
关于您的 C++ 代码,请勿使用 >> 运算符从二进制流中读取。将 read() 与要读取的 int 的地址一起使用。

int i;
fin.read((char*)&i, sizeof(int));

EDIT2
从封闭的流中读取也会导致未定义的行为。你不能调用 fin.close() 然后仍然期望能够从中读取。

【讨论】:

  • 一个 c/c++ char 可以处理 utf-8 字符串形式的 unicode。
【解决方案2】:

这可能与问题有关,也可能无关,但是...

当您创建 BinaryWriter 时,它默认以 UTF-8 格式写入 chars。这意味着其中一些可能会超过一个字节,从而导致您无法搜索。

您可以通过使用 2 参数构造函数来指定编码来避免这种情况。 System.Text.ASCIIEncoding 的实例与 C/C++ 默认使用的实例相同。

【讨论】:

  • ASCIIEncoding 的问题在于它默默地破坏了非 ASCII 字符。
  • 对于这样的互操作,您绝不能使用 C# 的字符串类型。除非您知道自己在做什么,否则现在情况并非如此。使用字节[]。即使是像我这样的高级程序员也害怕 String 类型。使用 Ecoding 变体将字符串转换为字节数组,然后写入其大小和数据。使用 Yacoby 的方法和一些 Unicode 库,如 icu,用 C++ 读回它。
【解决方案3】:

你的 C++ sn-p 有很多问题。您不应该将二进制阅读与格式化阅读混为一谈:

  // The file is closed after this line. It is WRONG to read from a closed file.
  fin.close();

  if(!strncmp("AB", memblock, 2)){ 
   printf("test. This works."); 
  }

  fin.seekg(2); // You are moving the "get pointer" of a closed file
  int i;

  // Even if the file is opened, you should not mix formatted reading
  // with binary reading. ">>" is just an operator for reading formatted data.
  // In other words, it is for reading "text" and converting it to a 
  // variable of a specific data type.
  fin >> i;

【讨论】:

  • 非常感谢。我很长时间没有使用这种东西了,需要指出这些:)
【解决方案4】:

如果有帮助的话,我介绍了 BinaryWriter 如何写入数据here

已经有一段时间了,但我会引用它并希望它是准确的:

  • Int16 写入为 2 个字节并填充。
  • Int32 写成 Little Endian 和零填充
  • 浮点数更复杂:它获取浮点值并取消对它的引用,获取十六进制的内存地址内容

【讨论】:

  • int32 是小端和 0 填充,这会导致一些问题吗?你能详细说明一下吗? (抱歉,链接还没查,里面可能会详细说明)
  • 看起来它与 C++ char 相关,与整数无关,除了偏移量
  • 找不到404页面。
  • @ChangmingSun 谢谢我已经更新了链接,如果你可以取消投票
  • @ChrisS,你的回答会吓到他。 Int16 和 Int32 就像浮点数一样,是可直接 blittable 的,因为它们是本机类型。 BinaryWriter 按原样编写它们,然后在相应的 C++ 类型中使用 memcpy()-ing 它们:int16_t、int32_t 和 float 是正确的方式。
猜你喜欢
  • 2020-02-20
  • 2020-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
  • 2018-08-21
相关资源
最近更新 更多