fstream — чтение двоичного файла в C ++ и вывод результата в виде 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.......

Вот краткий обзор двух файлов для удобства.

У меня такое чувство, что я читаю файлы. Я решил придерживаться библиотек 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, Если худшее приходит к худшему, я реализую метод, используя утилиты File IO в cstdio вместо. Если я не могу сделать это с помощью ifstream тогда я с удовольствием приму объяснение, чтобы я мог учиться!

0

Решение

Ваша проблема в том, что ваш char тип подписан. Поэтому, когда вы пишете (unsigned short)buffer[i], это переводится как char -> int -> unsigned short.

если байт меньше 0x7f, он рассматривается как> = 0, и все в порядке, но если нет, он внутренне дополняется 1 битом, чтобы сформировать отрицательное значение типа int. Вы первая проблема на 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)
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. Проблема похожа: когда значение char отрицательное, оно расширяется до отрицательного целого, а не положительного целого со значением более 127 (что isprint надеется).

Поэтому, если вы используете подписанные символы, вызов должен быть:

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

По вопросам рекламы [email protected]