c++ - 从二进制文件读取后出现奇怪的数字

标签 c++ hex decimal binaryfiles 16-bit

我想将十六进制的 32 位数字写入二进制文件,然后使用 reinterpret_cast 读取其中的一些并读取,例如16 位数字。我只读取 16 位,因为它决定了数据包的大小。在代码中有一个例子。也许问题出在大端或小端?

#include <iostream>     // std::cout
#include <fstream>      // std::ifstream
#include <cstdint>
#include <vector>

void saveTestData(void)
{
  std::vector<std::uint_fast32_t> tab
  {
    0x50e5495c, 0xe7b50200, 0xbe6b2248, 0x08004510,
    0x015c2340, 0x0000ff11, 0x1567c0a8, 0x004cc0a8,
    0x003de290, 0xc35a0148, 0x00000000, 0x01200003,
    0x00620000, 0x01140002, 0x00010000, 0x8000ef40,
    0x22560003, 0xe0042150, 0x00006bbf, 0xd67c800f,
    0x5b5b0003, 0xe0032150, 0x00006bbf, 0xd67c8007,
    0x1b5d0003, 0xe0022150, 0x00006bbf, 0xd67c800a,
    0xab5d0023, 0xe0052150, 0x00006bbf, 0xd67c8011,
    0x8b5c6bbf, 0xd67c8c55, 0xaf896bbf, 0xd67c8c90,
    0x4f896bbf, 0xd67c8cd4, 0xef8a6bbf, 0xd67c8d0d,
    0x1f8a6bbf, 0xd67c8d43, 0x7f886bbf, 0xd67c8d8f,
    0x8f896bbf, 0xd67c8dc4, 0xcf886bbf, 0xd67c8e19,
    0x6f896bbf, 0xd67c8e4e, 0x1f8a6bbf, 0xd67c8e82,
    0xcf8a6bbf, 0xd67c8ed7, 0x4f896bbf, 0xd67c8f0c,
    0xef896bbf, 0xd67c8f4f, 0x8f896bbf, 0xd67c8f96,
    0xef8a6bbf, 0xd67c8fdb, 0xcf896bbf, 0xd67c9008,
    0xbf89000e, 0x80001006, 0xf0724646, 0xb45b0000,
    0x00004646, 0xb45b0000, 0x00000000, 0x00000000,
    0x00000000, 0x00000000, 0x00004646, 0xb45b0000,
    0x00004646, 0xb45b0000, 0x00008000, 0x00000001,
    0x55550000, 0x0001aaaa, 0xaaaa0000, 0x01200003,
    0x00620000, 0x01140002, 0x00010000, 0x8000ef40,
    0x22560003, 0xe0042150, 0x0000
  };

  std::ofstream file;
  file.open("test.omgwtf", std::ofstream::binary);
  if(file.good())
  {
    file.write(reinterpret_cast<char*>(tab.data()), tab.size()*sizeof(std::uint_fast32_t));
    file.close();
  }

}

int main()
{
  saveTestData();
  std::ifstream file("test.omgwtf", std::ifstream::binary);
  if(file.good())
  {
    file.seekg(0, file.end);
    uint32_t length = file.tellg();
    file.seekg(0, file.beg);

    char *buffer = new char[length];
    std::cout << "length = " << length << std::endl;
    file.read(buffer, length);

    std::uint_fast32_t *number32 = reinterpret_cast<std::uint_fast32_t*>(buffer);
    std::cout << "1 number32 = " << *number32 << std::endl;     // ok

    number32 = reinterpret_cast<std::uint_fast32_t*>(buffer+4);
    std::cout << "2 number32 = " << *number32 << std::endl;     // ok

    // read 0xbe6b (16 bits not 32)
    // 0xbe6b (hex) = 48747 (dec)
    std::uint_fast16_t *number16 = reinterpret_cast<std::uint_fast16_t*>(buffer+8);
    std::cout << "3 number16 = " << *number16 << std::endl;     // not ok!? why?

    // read 2248 (16 bits not 32)
    // 2248 (hex) = 8776 (dec)
    number16 = reinterpret_cast<std::uint_fast16_t*>(buffer+10);
    std::cout << "4 number16 = " << *number16 << std::endl;     // not ok!? why?

    file.close();
    delete [] buffer;
  }
  return 0;
}

如何读 number16? 1,2 例子没问题。 3 例子应该是48747不是3194692168? 4 例子应该是 8776 而不是 1158725227?

clear; g++ test2.cpp -std=c++11 -o test2; ./test2

最佳答案

std::binary名字不好,它真的控制换行符翻译。

iostreams 仅用于文本。你可以有没有换行符翻译的文本(使用 std::binary ,文件以 Unix 换行符约定结束,仅 \n)或有换行符翻译的文本(不要使用 std::binary ,文件以操作系统约定结束,例如 \n\r\n ,甚至 \r )。

但即使是 std::binary , EOF 字符 (ASCII 26) 可能被识别并结束输入。或不。标准没说。该标准不提供任何未翻译文件访问机制。

人们一直在尝试设计一种更好的 C++ 标准 I/O 机制,将文件访问与文本处理分开,但还没有人让所有人都满意。

对于二进制文件,使用低级 I/O 机制。连<stdio.h>比 iostreams 更好(更少的翻译),但它仍然有一些。特定于操作系统的函数或使用底层操作系统函数的跨平台包装器(如 boost::asio )是二进制文件访问所需要的。

此外,到处都有严格的别名违规。不要使用 reinterpret_cast像那样,改用 memcpy或从输入文件中单独读取正确大小的 block 。

最后,您正在读取错误的大小变量。 uint_fast16_t不是 16 位,它是 16 位或更多,最快的。几乎可以肯定,32 位在您的 CPU 上比 16 位更快。如果你想要 16 位,使用 uint16_t .如果你想尽可能接近(但不少于)使用 uint_least16_t . uint_fast类型族适用于局部变量,例如循环计数器。由于大小未知,它们对 I/O 无用。

一旦你弄清楚了所有这些,你就需要担心原始数据的字节序,因为它是作为 32 位(或更多)值的序列写入的,无论高半部分还是低半部分写入文件首先是平台依赖。

关于c++ - 从二进制文件读取后出现奇怪的数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28752347/

相关文章:

java - 将十六进制转换为具有 3 位数字的 char

javascript - Angular 绑定(bind)打破了小数位

c++ - 在没有显式嵌套声明的情况下在另一个类的命名空间中声明一个类

c - 使用 scanf 获取用户输入的十六进制指令并将它们保存在 char 中。 (C)

c++ - rend指向哪里?

JavaScript - 将 UTF8 编码/解码为十六进制以及十六进制为 UTF8

c# - 小数超出范围 C# 和 SQL Server

c - C中的十进制转八进制

c++ - 下面的 std::move 是多余的吗?

c++ - 如何在 C++ 中实现多维映射的常量正确性