Чтение файла с разным типом данных, записанных в файл

Суть вопроса:

Имеется файл, записанный в формате 2 байта, 1 байт, 1 байт. Значения в файле всегда положительным (то есть unsigned) и записаны последовательно в одну строку. Требуется провести чтение файла и записать их в вектор. Как это можно реализовать?

Перепробовал уже множество способов, но так и не могу понять, как его корректно прочитать (и структурой пытался, и через переменные, и тд и тп, просто не понимаю свою проблему).

Чтение осуществлял через ifstream, но можно любой вариант использовать. Сам файл представлен в формате txt или dat.

Файл можно представить в виде такой последовательности введите сюда описание изображения


Ответы (2 шт):

Автор решения: Harry

В комментарий не влезу...
В чем проблема?

unsigned short var1;
unsigned char var2, var3;

ifstream in("....",ios::binary);

for(;;)
{
    in.read((char*)&var1,2);
    in.read((char*)&var2,1);
    in.read((char*)&var3,1);
    ....
}

Структура — ну, нужна ее корректная упаковка, на байт.

Что тут не так?

→ Ссылка
Автор решения: Stanislav Volodarskiy

Если уж решили читать байты, то надо стиснуть зубы и читать байты.

Байт в C++ - символ без знака. Можно читать просто символы, дальнейшая обработка будет осложнена.

Код ниже сразу читает одну запись в буфер. Затем из байтов конструируются значения. Так сделано потому что читать сразу в unsigned short плохая идея.

// так делать не надо!
unsigned short v1;
ifs.read(reinterpet_cast<char *>(&v1), 2);
  • unsigned short может оказаться длиннее двух байт. Тогда будет неопределённое поведение - в переменной останется мусор.
  • порядок байтов внутри unsigned short может быть разным. На разных машинах ваш код прочтет разные значения.
  • если вас беспокоит производительность, то чтение из файла куда медленнее битовых операций при конструировании значений.
  • если вы уверены что порядок байтов под контролем и не хотите ничего считать, то читайте прямо в uint16_t. У него размер ровно два байта - ни больше, ни меньше. Но я вас предупредил. (В комментарии указывают что этот способ опирается на размер байта в восемь бит, а он может отличаться. Ещё одна причина так не делать.)

Код:

#include <climits>
#include <iostream>
#include <fstream>

int main() {
    std::ifstream ifs("record", std::ios::binary);

    unsigned char buffer[4];
    // читайте запись целиком, так проще обработать ошибки, так быстрее
    // тут нужно приведение, зато дальше будет проще работать
    if (ifs.read(reinterpret_cast<char *>(buffer), sizeof buffer)) {
        // конструируем значение из байтов - защита от различного
        // порядка байтов на разных компьютерах
        unsigned short v1 = buffer[0] | (buffer[1] << CHAR_BIT);
        unsigned char v2 = buffer[2];
        unsigned char v3 = buffer[3];
        std::cout << v1 << ' '
                  << static_cast<int>(v2) << ' '
                  << static_cast<int>(v3) << '\n';
    } else {
        // всегда контролируйте успешность чтения!
        // не перечесть сколько возникает ошибок из-за чтения после конца файла
        std::cerr << "Error reading record\n";
    }
}
→ Ссылка