【问题标题】:Reading binary file in C++ and output result as hexdump在 C++ 中读取二进制文件并将结果输出为 hexdump
【发布时间】:2015-02-14 09:19:23
【问题描述】:

我正在为一个学校项目构建一个更简单的xxd 版本,并且在仅读取二进制文件时(即,当我读取纯文本文件时,一切都按预期工作)时,我被文件输出挂断了。

预期输出:

0000000: 504b 0304 1400 0000 0800 70b6 4746 562d  PK........p.GFV-
0000010: e841 3600 0000 3f00 0000 0900 1c00 706c  .A6...?.......pl
0000020: 6169 6e2e 7478 7455 5409 0003 7307 d754  ain.txtUT...s..T
0000030: ba1d d754 7578 0b00 0104 f501 0000 0414  ...Tux..........
0000040: 0000 000b c9c8 2c56 00a2 e2fc dc54 85e2  ......,V.....T..
0000050: c4dc 829c 5485 92d4 8a12 ae10 a844 625e  ....T........Db^
0000060: 7e49 466a 9142 4e66 5eaa 4266 9e02 9003  ~IFj.BNf^.Bf....
0000070: 56a0 9096 9993 ca05 0050 4b01 021e 0314  V........PK.....
0000080: 0000 0008 0070 b647 4656 2de8 4136 0000  .....p.GFV-.A6..
0000090: 003f 0000 0009 0018 0000 0000 0001 0000  .?..............
00000a0: 00a4 8100 0000 0070 6c61 696e 2e74 7874  .......plain.txt
00000b0: 5554 0500 0373 07d7 5475 780b 0001 04f5  UT...s..Tux.....
00000c0: 0100 0004 1400 0000 504b 0506 0000 0000  ........PK......
00000d0: 0100 0100 4f00 0000 7900 0000 0000       ....O...y.....

实际输出:

0000000: 504b 0304 1400 0000 0800 70ffb6 4746 562d  PK........p.GFV-
0000010: ffe841 3600 0000 3f00 0000 0900 1c00 706c  .A6...?.......pl
0000020: 6169 6e2e 7478 7455 5409 0003 7307 ffd754  ain.txtUT...s..T
0000030: ffba1d ffd754 7578 0b00 0104 fff501 0000 0414  ...Tux..........
0000040: 0000 000b ffc9ffc8 2c56 00ffa2 ffe2fffc ffdc54 ff85ffe2  ......,V.....T..
0000050: ffc4ffdc ff82ff9c 54ff85 ff92ffd4 ff8a12 ffae10 ffa844 625e  ....T........Db^
0000060: 7e49 466a ff9142 4e66 5effaa 4266 ff9e02 ff9003  ~IFj.BNf^.Bf....
0000070: 56ffa0 ff90ff96 ff99ff93 ffca05 0050 4b01 021e 0314  V........PK.....
0000080: 0000 0008 0070 ffb647 4656 2dffe8 4136 0000  .....p.GFV-.A6..
0000090: 003f 0000 0009 0018 0000 0000 0001 0000  .?..............
00000a0: 00ffa4 ff8100 0000 0070 6c61 696e 2e74 7874  .......plain.txt
00000b0: 5554 0500 0373 07ffd7 5475 780b 0001 04fff5  UT...s..Tux.....
00000c0: 0100 0004 1400 0000 504b 0506 0000 0000  ........PK......
00000d0: 0100 0100 4f00 0000 7900 0000 0000 0000  ....O...y.......

Here's a quick diff of the two files for easy reference.

我感觉这就是我阅读文件的方式。我决定坚持使用 C++ 库并使用std::ifstream 来读取文件。这是我的实现:

void DumpUtility::dump(const char* filename) {
    std::ifstream file(filename, std::ifstream::in|std::ifstream::binary); // open file for reading

    if(file.is_open()) { // ensure file is open and ready to go
        std::cout << std::hex << std::setfill('0'); // pad PC with leading zeros
        char buffer[this->bytesPerLine]; // buffer symbols

        while(file.good()) {
            file.read(buffer, this->bytesPerLine);
            std::cout << std::setw(7) << this->pc << ":";
            for(unsigned int i = 0; i < this->bytesPerLine; i++) {
                if(i % 2 == 0) std::cout << " ";

                std::cout << std::setw(2) << (unsigned short)buffer[i];
            }

            std::cout << "  ";
            for(unsigned int i = 0; i < this->bytesPerLine; i++) {
                if(isprint(buffer[i]) == 0) { // checks if character is printable
                    std::cout << ".";
                } else {
                    std::cout << buffer[i];
                }
            }
            std::cout << std::endl;
            this->pc += this->bytesPerLine;
        }
    } else {
        std::cerr << "Couldn't open file. General error..." << std::endl;
        exit(EXIT_FAILURE);
    }

    file.close();
}

所以,file.read(buffer, this-&gt;bytesPerLine); 是读取文件的行,我通过iomanip 将数据格式化为十六进制。我也尝试过使用printf(%02X, (unsigned short)buffer[i]);,但没有成功——同样的输出。

做了什么

  • 使用多个编译器重新编译程序
    • clang++ -O0 -g -Wall -c
    • g++ -g -Wall -c
  • g++ 的两个版本
    • 4.2.1 - OS X 10.10
    • 3.4.6 - Sun Solaris 10
  • lldbgdb 中进行调试以查看这些额外F 的确切来源,但我一无所获。

似乎std::ifstream::read() 正在做一些事情,而不是简单地按原样存储特殊字符。有谁知道这些额外的F's 代表什么,谁能指出我正确的方向来解决这个问题?

注意:我试图了解如何使用 std::ifstream 而不是使用 cstdio 来做到这一点。如果最坏的情况发生,那么我将使用 cstdio 中的文件 IO 实用程序来实现该方法。如果我不能使用ifstream 做到这一点,那么我很乐意接受解释,以便我可以学习!

【问题讨论】:

  • 你的基本循环不正确;该文件可能是good,当没有更多内容可阅读时。如果输入文件不是bytes_per_line 的倍数会怎样?循环应该是while ( file.read( buffer, bytes_per_line ) || file.gcount() &gt; 0 );您还想输出file.gcount() 字节,而不是bytes_per_line。 (实际上,这是while ( file.good() ) 可以工作的一种情况,提供您之后使用file.gcount() 来获取实际输入的字节数。)
  • 另外:char buffer[this-&gt;bytesPerLine]; 不是合法的 C++;你需要std::vector&lt;char&gt; buffer( bytesPerLine );。 (或者,使 bytesPerLine 成为 static int const 成员,在类定义中初始化。)
  • @JamesKanze char 指针似乎在没有任何警告的情况下工作。为什么这可能是错误的?
  • C 样式数组的维度必须是编译时常量。如果您的代码可以编译,则说明您使用的是特定编译器中的扩展。
  • 明白了。我在 Solaris 上将编译器切换到 CC 并发现很难。感谢您的建议,我很感激!

标签: c++ fstream binaryfiles hexdump


【解决方案1】:

您的问题是您的 char 类型已签名。所以你写(unsigned short)buffer[i]的时候,翻译成char -> int -> unsigned short。

如果字节在 0x7f 以下,则视为 >=0 并且一切正常,但如果不是,则在内部用 1 位填充以形成负整数。你的第一个问题是b6。实际发生的是:

b6 (signed char) -> FFFFFFb6 (signed int) -> FFB6 (unsigned short)

希望修复很简单。你只需要写:

std::cout << std::setw(2) << (unsigned short) (unsigned char) buffer[i];

因为现在转换将正确:

b6 (signed char) -> b6 (unsigned char) -> b6 (signed int) -> b6 (unsigned short)

【讨论】:

  • 几个cmets:首先,unsigned short 没有意义; unsigned 也可以。其次,另一种方法是使用unsigned char 作为缓冲区(或缓冲区指针),然后在那里进行转换。 (我更喜欢你的解决方案,但两者都是有效的。)最后,另一种选择是使用 buffer[i] &amp; 0xFF,并完全放弃强制转换。
  • @JamesKanze,你说得对!我能够按位和 0x7F 完成同样的事情!
  • @djthoms Anding 0x7F 会改变一些字节的值。
  • @JamesKanze 是的,我在测试时发现了这一点。
【解决方案2】:

尝试以下方法:

std::cout << std::setw(2) << (unsigned short)buffer[i] & 0xFF;

或者

std::cout << std::setw(2) << (unsigned short)(unsigned char)buffer[i];

或者

unsigned char buffer[this->bytesPerLine];
...
std::cout << (char)buffer[i];

另外,isprint 与有符号字符一起使用不正确。问题类似:当 char 值为负时,它会扩展为负整数,而不是值超过 127 的正整数(isprint 期望的值)。

因此,如果您使用签名字符,则调用应为:

if(isprint((unsigned char)buffer[i]) == 0)

【讨论】:

  • 为什么要在第一个提议的解决方案中使用演员表?
  • @JamesKanze 因为类型转换通过符号扩展。
  • 但是&amp; 0xFF 几乎消除了任何迹象;它只需要低 8 位。
  • @JamesKanze 确实如此。但是从unsigned char 转换为任何其他(更大的)序数类型不需要&amp; 操作。
  • 无操作时,需要铸件。使用&amp; 0xFF 的全部意义在于它不需要任何显式类型转换。 (当没有unsigned char 类型时,这是解决此问题的常用方法。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-14
  • 1970-01-01
相关资源
最近更新 更多