在 C++ 中读取二进制文件并将结果输出为 hexdump
Reading binary file in C++ and output result as hexdump
我正在为一个学校项目构建一个更简单的 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->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
- 在
lldb
和 gdb
中调试以查看这些额外 F 的确切来源,但我一无所获。
似乎 std::ifstream::read()
正在做一些事情,而不是简单地按原样存储特殊字符。有谁知道这些额外的 F's
代表什么,谁能指出我正确的方向来解决这个问题?
注意: 我正在尝试了解如何使用 std::ifstream
而不是使用 cstdio
来做到这一点。如果最坏的情况发生,那么我将改为使用 cstdio
中的文件 IO 实用程序来实现该方法。如果我不能使用 ifstream
做到这一点,那么我很乐意接受解释,这样我就可以学习了!
尝试以下操作:
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
与 signed char 一起使用不正确。问题类似:当 char 值为负时,它会扩展为负整数,而不是大于 127 的正整数(isprint
所期望的)。
因此,如果您使用的是有符号字符,调用应为:
if(isprint((unsigned char)buffer[i]) == 0)
您的问题是您的 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)
我正在为一个学校项目构建一个更简单的 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->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
- 在
lldb
和gdb
中调试以查看这些额外 F 的确切来源,但我一无所获。
似乎 std::ifstream::read()
正在做一些事情,而不是简单地按原样存储特殊字符。有谁知道这些额外的 F's
代表什么,谁能指出我正确的方向来解决这个问题?
注意: 我正在尝试了解如何使用 std::ifstream
而不是使用 cstdio
来做到这一点。如果最坏的情况发生,那么我将改为使用 cstdio
中的文件 IO 实用程序来实现该方法。如果我不能使用 ifstream
做到这一点,那么我很乐意接受解释,这样我就可以学习了!
尝试以下操作:
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
与 signed char 一起使用不正确。问题类似:当 char 值为负时,它会扩展为负整数,而不是大于 127 的正整数(isprint
所期望的)。
因此,如果您使用的是有符号字符,调用应为:
if(isprint((unsigned char)buffer[i]) == 0)
您的问题是您的 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)