У меня есть программа, которая работает на Intel Edison (32-битный Yocto Linux). Он считывает данные датчика, а затем записывает эти данные датчика в файл. Данные поступают в пакетах 1 int и 13 double, при этом 100 пакетов поступают каждую секунду. Через некоторое время я буду извлекать из этого файлы и читать их с помощью инструмента, работающего на компьютере с Windows x64.
На данный момент я пишу данные в виде необработанного текстового файла (поскольку строки удобны и переносимы). Однако из-за объема данных, которые будут записаны для этого, я ищу способы сэкономить место. Однако я пытаюсь найти способ сделать так, чтобы никакие данные не были потеряны при интерпретации этого с другой стороны.
Моя первоначальная идея заключалась в том, чтобы создать структуру, которая выглядит следующим образом:
struct dataStruct{
char front;
int a;
double b, c, d, e, f, g, h, i, j, l, m, n, o;
char end;
}
и затем сделайте объединение этого следующим образом:
union dataUnion{
dataStruct d;
char[110] c;
}
//110 was chosen because an int = 4 char, and a double = 8 char,
//so 13*8 = 104, and therefore d = 1 + 4 + 13*8 + 1 = 110
а затем записать массив символов в файл. Тем не менее, небольшое прочтение подсказывает мне, что подобная реализация не обязательно может быть совместимой с ОС (хуже … она может работать иногда, а иногда нет).
Поэтому мне интересно — есть ли переносимый способ сохранить эти данные, не сохраняя их в виде необработанного текста?
Как говорили другие: сериализация, вероятно, лучшее решение для вашей проблемы.
Поскольку вы находитесь в среде с ограниченными ресурсами, я предлагаю использовать что-то вроде MsgPack. Это только заголовок (предоставляется компилятор C ++ 11), довольно легкий, формат прост, и интерфейс C ++ хорош. Он даже позволяет очень легко сериализовать пользовательские типы (т.е. классы / структуры):
// adapted from https://github.com/msgpack/msgpack-c/blob/master/QUICKSTART-CPP.md
#include <msgpack.hpp>
#include <vector>
#include <string>
struct dataStruct {
int a;
double b, c, d, e, f, g, h, i, j, l, m, n, oo; // yes "oo", because "o" clashes with msgpack :/
MSGPACK_DEFINE(a, b, c, d, e, f, g, h, i, j, l, m, n, oo);
};
int main(void) {
std::vector<dataStruct> vec;
// add some elements into vec...
// you can serialize dataStruct directly
msgpack::sbuffer sbuf;
msgpack::pack(sbuf, vec);
msgpack::unpacked msg;
msgpack::unpack(&msg, sbuf.data(), sbuf.size());
msgpack::object obj = msg.get();
// you can convert object to dataStruct directly
std::vector<dataStruct> rvec;
obj.convert(&rvec);
}
В качестве альтернативы вы можете проверить Google FlatBuffers. Кажется, это довольно эффективно, но я еще не пробовал.
РЕДАКТИРОВАТЬВот полный пример, иллюстрирующий весь цикл сериализации — файловый ввод / вывод — десериализация:
// adapted from:
// https://github.com/msgpack/msgpack-c/blob/master/QUICKSTART-CPP.md
// https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_unpacker#msgpack-controls-a-buffer
#include <msgpack.hpp>
#include <fstream>
#include <iostream>
using std::cout;
using std::endl;
struct dataStruct {
int a;
double b, c, d, e, f, g, h, i, j, l, m, n, oo; // yes "oo", because "o" clashes with msgpack :/
MSGPACK_DEFINE(a, b, c, d, e, f, g, h, i, j, l, m, n, oo);
};
std::ostream& operator<<(std::ostream& out, const dataStruct& ds)
{
out << "[a:" << ds.a << " b:" << ds.b << " ... oo:" << ds.oo << "]";
return out;
}
int main(void) {
// serialize
{
// prepare the (buffered) output file
std::ofstream ofs("log.bin");
// prepare a data structure
dataStruct ds;
// fill in sample data
ds.a = 1;
ds.b = 1.11;
ds.oo = 101;
msgpack::pack(ofs, ds);
cout << "serialized: " << ds << endl;
ds.a = 2;
ds.b = 2.22;
ds.oo = 202;
msgpack::pack(ofs, ds);
cout << "serialized: " << ds << endl;
// continuously receiving data
//while ( /* data is being received... */ ) {
//
// // initialize ds...
//
// // serialize ds
// // You can use any classes that have the following member function:
// // https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_packer#buffer
// msgpack::pack(ofs, ds);
//}
}
// deserialize
{
// The size may decided by receive performance, transmit layer's protocol and so on.
// prepare the input file
std::ifstream ifs("log.bin");
std::streambuf* pbuf = ifs.rdbuf();
const std::size_t try_read_size = 100; // arbitrary number...
msgpack::unpacker unp;
dataStruct ds;
// read data while there are still unprocessed bytes...
while (pbuf->in_avail() > 0) {
unp.reserve_buffer(try_read_size);
// unp has at least try_read_size buffer on this point.
// input is a kind of I/O library object.
// read message to msgpack::unpacker's internal buffer directly.
std::size_t actual_read_size = ifs.readsome(unp.buffer(), try_read_size);
// tell msgpack::unpacker actual consumed size.
unp.buffer_consumed(actual_read_size);
msgpack::unpacked result;
// Message pack data loop
while(unp.next(result)) {
msgpack::object obj(result.get());
obj.convert(&ds);
// use ds
cout << "deserialized: " << ds << endl;
}
// All complete msgpack message is proccessed at this point,
// then continue to read addtional message.
}
}
}
Выход:
serialized: [a:1 b:1.11 ... oo:101]
serialized: [a:2 b:2.22 ... oo:202]
deserialized: [a:1 b:1.11 ... oo:101]
deserialized: [a:2 b:2.22 ... oo:202]
Вам нужно сериализовать ваши данные. И поскольку я могу предположить, что повышение не является вариантом, вам придется сделать это вручную.
Истинная переносимость (за исключением неподписанных) это головная боль. Однако если вы знаете, что все системы, которые вы используете, все используют одну и ту же кодировку (например, дополнение двух для целых чисел со знаком и IEE754 для числа с плавающей запятой), то вам повезло, и вы можете сделать это, используя базовые битовые манипуляции.
Вам нужно было бы установить побайтный буфер, используя маски.
Единственное, что вам нужно сделать по-другому, зависит от байтовой последовательности машин.
Не изобретай велосипед. Это такие вещи, которые Google Буферы протокола были разработаны для решения — передачи четко определенных данных между компьютерами таким образом, чтобы они не были удобочитаемыми. (Например, в отличие от JSON или XML)
В качестве альтернативы вы могли бы пойти настоящий старый skool и читать дальше ASN.1
И для полноты здесь Сравнение форматов сериализации данных, так что не стесняйтесь выбирать свой яд.
Лучший способ — использовать сериализацию (ProtoBuf, Thrift и т. Д.). Но если вы не можете использовать его и нуждаетесь в «сыром» решении, единственный способ — описать ваши структуры, используя специальные типы, которые имеют одинаковый размер на всех платформах:
struct dataStruct{
uint32_t a; // see cstdint.h or boost
/// ...
}
Вы должны быть осторожны с порядком байтов тоже. Таким образом, вы всегда должны преобразовывать все поля в порядке с прямым порядком байтов (или с прямым порядком байтов) всякий раз, когда вы сериализуете его (передаете на «другую» сторону или сохраняете в файл).
Еще одна вещь, которую нужно помнить, это структура упаковки))
#pragma pack(1)
или же
__attribute__((packed))
Это широкая тема, поэтому самое простое решение — использовать сериализаторы.
Я бы сказал, что текст самый безопасный. Сохранение его как raw int и doubles открывает перед вами проблему с прямым и обратным порядком байтов и, возможно, проблемы с форматами двойной компоновки. Даже переход к тексту может вызвать проблемы, если вы не отделяете различные значения друг от друга.
Альтернатива состоит в том, чтобы определить собственный «универсальный» формат и перевести его в / из этого в ваших операциях записи / чтения … возможно, вывести int как текст, но как текст псевдонаучной нотации сказать 5-значное значение мантиссы, ‘e’ и показатель 2/3 символа.