Преобразование типов данных для добавления в QByteArray для записи необработанных данных в файл - PullRequest
0 голосов
/ 31 октября 2018

У меня есть несколько типов данных, которые я пытаюсь записать в файл необработанных данных. Я не использую QDataStream, потому что он записывает некоторую дополнительную информацию о данных, такую ​​как длина и порядок данных. Мне просто нужен файл, который состоит только из байтов, которые я пишу, и будет интерпретирован только тем, кто знает правильный порядок и размер записываемых типов данных.

Я использую QFile с методом записи qint64 QIODevice::write(const QByteArray &byteArray).

У меня есть несколько uint8_t, uint32_t и float для записи в файл. Как я могу преобразовать эти данные в QByteArray без лишних байтов?

Вот что у меня есть:

void FileWriter::writeData(uint8_t status, uint8_t channel, uint32_t ticks, float source0, float source1){

    QByteArray dataToWrite;

    //some code to add the paramters to the dataToWrite array

    this->file.write(dataToWrite);
}

Обычное приведение старого стиля к char дает мне ошибку implicit conversion changes signedness, так что это не будет правильно хранить значение.

Как правильно копировать эти значения в QByteArray, чтобы я мог записать его в файл?

1 Ответ

0 голосов
/ 31 октября 2018

Как правило, вы хотите сериализовать свои потоки данных, сохраняя при этом порядок данных.

uint8_t переменные могут быть преобразованы в байты с помощью простого static_cast. Для больших целочисленных типов необходимо отслеживать порядок байтов данных и передавать их побайтно.

float типы данных немного сложнее. Сначала необходимо представить биты с плавающей точкой как целое значение (uint32_t), а затем сериализовать оттуда. Пример может быть показан ниже:

QByteArray data;

//serialize a uint8_t
data.push_back(static_cast<char>(status));

//serialize a uint32_t, little-endian
data.push_back(static_cast<char>((ticks) & 0xFF); //lowest-order byte
data.push_back(static_cast<char>((ticks >> 8) & 0xFF));
data.push_back(static_cast<char>((ticks >> 16) & 0xFF));
data.push_back(static_cast<char>((ticks >> 24) & 0xFF)); //highest-order byte

//serialize a float, by first representing the bits as a uint32_t: 

static_assert(sizeof(float) == sizeof(uint32_t), "Floats should be 4 bytes");
uint32_t rep;
std::memcpy(&rep, &source0, sizeof(float)); //using memcpy here so that we don't violate strict aliasing. 
data.push_back(static_cast<char>((rep) & 0xFF);
data.push_back(static_cast<char>((rep >> 8) & 0xFF));
data.push_back(static_cast<char>((rep >> 16) & 0xFF));
data.push_back(static_cast<char>((rep >> 24) & 0xFF));

this->file.write(data);

Несколько вещей, которые нужно иметь в виду:

  1. Порядковый номер ваших данных важен. Не все системы имеют одинаковый порядок байтов, поэтому вам необходимо четко указать в своей документации, как именно ориентированы байты.

  2. Некоторые люди пытаются преобразовать биты float в uint32_t, написав что-то вроде uint32_t rep = *reinterpret_cast<uint32_t*>(&source0);. Не делайте этого - это нарушение Strict Aliasing Rule .

  3. Хорошей идеей может быть сериализация номера версии в качестве первой части вашего файла, чтобы вы могли вносить будущие изменения в формат данных и иметь обратную совместимость.

  4. Большая часть этой ручной битовой упаковки может быть записана в функции шаблона, которые будут делать это автоматически. Это оставлено читателю в качестве упражнения.

  5. Если важна производительность, вы должны выяснить, насколько большими будут данные, и reserve() пространство в байтовом массиве перед отправкой - это избавит массив от необходимости перераспределять и расти снова и снова.

...