【问题标题】:Reading bytes in c++在 C++ 中读取字节
【发布时间】:2012-10-04 06:57:29
【问题描述】:

我正在尝试从二进制文件中读取字节,但没有成功。 我尝试了很多解决方案,但没有得到任何结果。 文件结构:

[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000803(2051) magic number 
0004     32 bit integer  60000            number of images 
0008     32 bit integer  28               number of rows 
0012     32 bit integer  28               number of columns 
0016     unsigned byte   ??               pixel 
0017     unsigned byte   ??               pixel 
........ 
xxxx     unsigned byte   ??               pixel

我是如何尝试的(不起作用):

auto myfile = fopen("t10k-images.idx3-ubyte", "r");
char buf[30];
auto x = fread(buf, 1, sizeof(int), myfile);

【问题讨论】:

  • 它说“MSB first”的部分有点重要。
  • “没有成功”是什么意思?我相信这应该将 sizeof(int) 字节读入缓冲区。您应该在读取后检查 x 以确保它等于 x == sizeof(int)。尝试将缓冲区打印为十六进制,看看它是否正确读取。
  • 我猜问题是字节序。如果磁盘上的ints 是大端,而系统是小端,那么数字将不匹配。
  • 具有讽刺意味的是,我是在像你一样无法阅读 MNIST 手写数字数据库后来到这里的。

标签: c++ c++11 binary byte


【解决方案1】:

读取字节为unsigned char:

ifstream if;

if.open("filename", ios::binary);

if (if.fail())
{
    //error
}

vector<unsigned char> bytes;

while (!if.eof())
{
    unsigned char byte;

    if >> byte;

    if (if.fail())
    {
        //error
        break;
    }

    bytes.push_back(byte);
}

if.close();

然后将多个字节转为32-bit integer 例如:

uint32_t number;

number = ((static_cast<uint32_t>(byte3) << 24)
    | (static_cast<uint32_t>(byte2) << 16) 
    | (static_cast<uint32_t>(byte1) << 8) 
    | (static_cast<uint32_t>(byte0)));

这应该涵盖字节序问题。 int 在系统上显示为 B0B1B2B3B3B2B1B0 并不重要,因为转换是通过位移来处理的。该代码不假定内存中的任何特定顺序。

【讨论】:

  • 这可能是您分配字节的顺序。我这里不给你设置byte0byte1等。这是你必须要做的事情。
  • 是的,我已经按顺序替换了字节并且它可以工作。抱歉,我无法将两个答案都标记为正确。
【解决方案2】:

C++ 流库函数read() 可用于二进制文件I/O。鉴于链接中的代码示例,我会这样开始:

std::ifstream myfile("t10k-images.idx3-ubyte", std::ios::binary);
std::uint32_t magic, numim, numro, numco;

myfile.read(reinterpret_cast<char*>(&magic), 4);
myfile.read(reinterpret_cast<char*>(&numim), 4);
myfile.read(reinterpret_cast<char*>(&numro), 4);
myfile.read(reinterpret_cast<char*>(&numco), 4);

// Changing byte order if necessary
//endswap(&magic);
//endswap(&numim);
//endswap(&numro);
//endswap(&numco);

if (myfile) {
    std::cout << "Magic = "  << magic << std::endl
              << "Images = " << numim << std::endl
              << "Rows = "   << numro << std::endl
              << "Cols = "   << numco << std::endl;
}

如果字节顺序(字节序)应该反转,您可以编写一个简单的反转函数,如下所示:endswap()

【讨论】:

  • 我得到类似 50855936、270991360、469762048、469762048 的信息。所以这种方法不起作用。
  • @wsevendays:这与 Geoff_Montee 的答案相同,在这里您还得到了 50855936(其他字节顺序)。试试链接中给出的endswap 函数!
【解决方案3】:

了解文件布局的字节序,因此读取多字节数字很重要。假设 big-endian 是 always 的书面格式,并且假设值确实是 32 位无符号值:

uint32_t magic = 0;
unsigned char[4] bytes;
if (1 == fread(bytes, sizeof(bytes), 1, f))
{
   magic = (uint32_t)((bytes[0] << 24) | 
                      (bytes[1] << 16) | 
                      (bytes[2] << 8) | 
                      bytes[3]);
}

注意:无论阅读器(您的程序)是小端还是大端,这都会起作用。我敢肯定我错过了至少一个演员,但希望你明白这一点。读取多字节数字的唯一安全且可移植的方法是(a)了解它们的字节顺序,以及(b)读取和组装它们一个字节一个字节。

【讨论】:

  • 我将每个字节转换为unit32_t,然后在我的答案中移动每个字节。不确定编译器是否会为每个班次自动提升它们。
  • 你不是唯一一个。语言方面的人会比我知道的更多,但我通常会像你一样做(每个值在轮班之前提升)。需要大量打字,但有效。两个我都见过。有关相关转换,请参阅this example。 (我同意你的回答,我同意它)。
  • @WhozCraig 你的方法有效!但如上所示,“magic”为 0。现在我得到“2051”,这是我需要的结果。
  • @wsevendays Geoff 的也应该如此
  • @wsevendays 魔法在上面初始化为零,读取之前。养成这个习惯,顺便说一句。以后会很好地为您服务。
【解决方案4】:

这是从文件中读取 uint32_t 的方式:

auto f = fopen("", "rb"); // not the b, for binary files you need to specify 'b'

std::uint32_t magic = 0;
fread (&magic, sizeof(std::uint32_t), 1, f);

希望这会有所帮助。

【讨论】:

  • 这不包括任何潜在的字节序问题。
猜你喜欢
  • 2012-03-23
  • 1970-01-01
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
  • 1970-01-01
  • 2011-07-01
  • 2014-04-23
相关资源
最近更新 更多