1

我一直在互联网上搜索一种在 C++ 中读取二进制文件的方法,我发现了两个这样的片段:

1号:

#include <iostream>
#include <fstream>

int main(int argc, const char *argv[])
{
   if (argc < 2) {
      ::std::cerr << "Usage: " << argv[0] << "<filename>\n";
      return 1;
   }
   ::std::ifstream in(argv[1], ::std::ios::binary);
   while (in) {
      char c;
      in.get(c);
      if (in) {
         // ::std::cout << "Read a " << int(c) << "\n";
         printf("%X ", c);
      }
   }
   return 0;
}

结果:

6C 1B 1 FFFFFFDC F FFFFFFE7 F 6B 1 

2号:

#include <stdio.h>
#include <iostream>

using namespace std;

// An unsigned char can store 1 Bytes (8bits) of data (0-255)
typedef unsigned char BYTE;

// Get the size of a file
long getFileSize(FILE *file)
{
    long lCurPos, lEndPos;
    lCurPos = ftell(file);
    fseek(file, 0, 2);
    lEndPos = ftell(file);
    fseek(file, lCurPos, 0);
    return lEndPos;
}

int main()
{
    const char *filePath = "/tmp/test.bed";
    BYTE *fileBuf;          // Pointer to our buffered data
    FILE *file = NULL;      // File pointer

    // Open the file in binary mode using the "rb" format string
    // This also checks if the file exists and/or can be opened for reading correctly
    if ((file = fopen(filePath, "rb")) == NULL)
        cout << "Could not open specified file" << endl;
    else
        cout << "File opened successfully" << endl;

    // Get the size of the file in bytes
    long fileSize = getFileSize(file);

    // Allocate space in the buffer for the whole file
    fileBuf = new BYTE[fileSize];

    // Read the file in to the buffer
    fread(fileBuf, fileSize, 1, file);

    // Now that we have the entire file buffered, we can take a look at some binary infomation
    // Lets take a look in hexadecimal
    for (int i = 0; i < 100; i++)
        printf("%X ", fileBuf[i]);

    cin.get();
    delete[]fileBuf;
        fclose(file);   // Almost forgot this
    return 0;
}

结果:

6C 1B 1 DC F E7 F 6B 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A1 D 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

结果xxd /tmp/test.bed

0000000: 6c1b 01dc 0fe7 0f6b 01                   l......k.

的结果ls -l /tmp/test.bed

-rw-rw-r-- 1 user user 9 Nov  3 16:37 test.bed

第二种方法是在开始时给出正确的十六进制代码,但似乎文件大小错误,第一种方法是弄乱字节。

这些方法看起来很不一样,也许在c++中有很多方法可以做同样的事情?有没有专业人士采用的成语?

4

4 回答 4

1

您的两种方法都是 C 和 C++ 的某种奇怪组合(嗯,实际上第二种方法只是普通的 C);尽管如此,第一种方法基本上是正确的,但您必须使用unsigned charfor c,否则任何超过 0x7f 的字节都被读取为负数,从而导致错误的输出。1

要以“C++ 方式”正确地做事您应该这样做:

std::cout<<std::hex<<std::setfill('0');

...

   if (in)
      std::cout << std::setw(2)<<int(c) << "\n";

第二个得到正确的“签名”,但它主要只是 C。一个快速修复将是修复100循环for,将其替换为fileSize. 但总的来说,将整个文件加载到内存中只是为了以十六进制转储其内容是一个拙劣的想法。您通常做的是在固定大小的缓冲区中一次读取文件,然后按 by 进行转换。


  1. get返回一个int;如果它大于分配时0x7f溢出char,通常会导致一些负值。然后,当它被传递给printf它时,它会被符号扩展(因为传递给可变参数函数的任何有符号整数参数都被扩展为int)但被解释为unsigned int由于%X参数。(所有这些假设 2 的补码算术,非信号整数溢出和有符号char
于 2013-11-03T22:30:06.910 回答
1

您当然希望在将char对象unsigned char处理为整数值之前将其转换为!问题是char可能会签名,在这种情况下,当您转换负值int时,它们会被转换为负值。显示为十六进制的负数int将有两个以上的十六进制数字,前导的可能都是“f”。

我没有立即发现为什么第二种方法的尺寸错误。但是,读取二进制文件的 C++ 方法很简单:

#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>

std::vector<unsigned char> bytes;
{
    std::ifstream in(name, std::ios_base::binary);
    bytes.assign(std::istreambuf_iterator<char>(in >> std::noskipws),
                 std::istreambuf_iterator<char>());
}
std::cout << std::hex << std::setfill('0');
for (int v: bytes) {
    std::cout << std::setw(2) << v << ' ';
}
于 2013-11-03T22:24:24.090 回答
0

在第一种情况下,您正在打印 char(已签名),而在第二种情况下,您正在对 unsigned char 执行相同的操作。%X 将字符扩展为整数,这会导致差异。

于 2013-11-03T22:24:01.897 回答
0

在搜索为什么@Roland Illig 的答案(现已删除)不起作用时,我找到了以下解决方案,不确定它是否符合专业标准,但到目前为止它给出了正确的结果,并允许检查开头 n - 文件的字节数:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>


int main(int argc, const char *argv[])
{
    if (argc < 3) {
        ::std::cerr << "usage: " << argv[0] << " <filename>\n";
        return 1;
    }

    int nbytes = std::stoi(argv[2]);
    char buffer[nbytes];
    std::streamsize size = nbytes;

    std::ifstream readingFile(argv[1], std::ios::binary);
    readingFile.read(buffer, (int)size);
    std::streamsize bytesread = readingFile.gcount();
    unsigned char rawchar;
    if (bytesread > 0) {
        for (int i = 0; i < bytesread; i++) {
            rawchar = (unsigned char) buffer[i];
            printf("%02x ", (int) rawchar);
        }
        printf("\n");
    }

    return 0;
}

我从 wibit.com 得到的另一个答案:

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, const char* argv[])
{
  ifstream inBinaryFile;
  inBinaryFile.open(argv[1], ios_base::binary);
  int currentByte = inBinaryFile.get();
  while(currentByte >= 0)
  {
    printf("%02x ", currentByte);
    currentByte = inBinaryFile.get();
  }
  printf("\n");
  inBinaryFile.close();
  return 0;
}
于 2013-11-06T22:00:43.530 回答