C ++: Как прочитать определенное количество байтов из двоичного файла и сохранить его в uint16_t? - PullRequest
0 голосов
/ 10 марта 2019

Итак, у меня возникла эта проблема, я не очень разбираюсь в работе с файлами, и я только начал заниматься классом C ++ в колледже, и для первого задания мне нужно извлечь конкретные байты из двоичного файла, а затем продолжить работу с ними.

Двоичный файл выглядит примерно так:

01001001 01001001 00000101 00000000 00001010 00000000 00001100 00000000
00101101 00101110 00101111 00110000 00110001 00101000 00101001 00101010
...

Где первые 8B - это значения 2B некоторых управляющих свойств.(т. е. 0-2B - это ширина, 2-4B - это высота и т. д.) И мне нужно прочитать эти свойства 2B по одному и сохранить их значения в переменных uint16_t.Дальнейшее, что я получил до сих пор, было с этим:

int main(void) {
  fstream f1;
  uint16_t endian, width, height, format;

  f1.open("input_01.img", ios::in | ios::binary);

  f1.get((char*) &endian, 2);
  f1.get((char*) &width, 2);
  f1.get((char*) &height, 2);
  f1.get((char*) &format, 2);

  /*...*/
}

Этот код правильно читает первые 2B, но затем по какой-то причине он перестает работать ... Я не уверен, если я непонять, как работает функция get () или мой подход совершенно неверный ...

1 Ответ

2 голосов
/ 10 марта 2019

Вам нужно использовать read, а не get. См http://www.cplusplus.com/reference/istream/istream/read/ и http://www.cplusplus.com/reference/istream/istream/get/

Там ясно сказано:

2) c-строка Извлекает символы из потока и сохраняет их в s в виде c-строки, пока либо (n-1) символов не будут извлечены, либо встречается символ-разделитель: символ-разделитель быть либо символом новой строки ('\ n'), либо разделителем (если этот аргумент уточняется). Символ-разделитель не извлекается из входная последовательность, если найдена, и остается там как следующий символ извлечено из потока (см. getline для альтернативы, которая делает откажитесь от символа-разделителя). Нулевой символ ('\ 0') автоматически добавляется к записанной последовательности, если n больше чем ноль, даже если извлекается пустая строка.

Так что у вас будут проблемы, потому что символы новой строки и нуль являются действительными символами как часть числа в двоичном виде. Кроме того, поскольку, как сказал tkausl, вы читаете 4 байта вместо 2. А также потому, что get добавляет нулевое значение после.

Вы можете сделать:

int main(void) {
  fstream f1;
  uint16_t endian, width, height, format;

  f1.open("input_01.img", ios::in | ios::binary);

  f1.read((char*) &endian, sizeof(endian));
  f1.read((char*) &width, sizeof(width));
  f1.read((char*) &height, sizeof(height));
  f1.read((char*) &format, sizeof(format));

  /*...*/
}

Таким образом, компилятор устанавливает размер для вас, учитывая, что uint16_t является правильным.

Или даже использовать шаблон:

template <class T, class U>
void readFromFile(T& f1, U& variable)
{
    f1.read((char*) &variable, sizeof(variable));
}

И затем вызвать его с помощью:

  f1.open("input_01.img", ios::in | ios::binary);

  readFromFile(f1, endian);
  readFromFile(f1, width);
  readFromFile(f1, height);
  readFromFile(f1, format);

Другой способ, полезный, когда вам нужно прочитать несколько вещей:

// Attribute packed works on GCC, on other compilers you 
// might need a pragma
struct __attribute__((packed)) FileStruct
{
    uint16_t endian= 0;
    uint16_t width= 0;
    uint16_t height= 0;
    uint16_t format= 0;
};
// We check structure size after declaring it; should be 8 bytes long
static_assert(sizeof(FileStruct) == 8, "Size error");

Тогда:

  FileStruct f;
  f1.open("input_01.img", ios::in | ios::binary);
  readFromFile(f1, f); // f.width has the width, f.format has format, etc.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...