试图找出一种可移植的数据保存方法
Trying to figure out a portable data saving approach
我有一个程序正在英特尔Edison(32位Yocto Linux)上运行。它读取传感器数据,然后将传感器数据写入文件。数据以1 int和13 double的数据包形式出现,每秒有100个数据包到达。一段时间后,我将从中提取文件,并使用x64 windows计算机上运行的工具读取这些文件。
目前,我正在将数据作为一个原始文本文件来编写(因为字符串很好,而且是可移植的)。然而,由于将为此编写大量数据,我正在寻找节省空间的方法。然而,我正在努力想办法做到这一点,这样在另一边的解释中就不会丢失任何数据。
我最初的想法是继续创建一个看起来像这样的结构:
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
然后将char数组写入文件。然而,阅读告诉我,这样的实现可能不一定在操作系统之间兼容(更糟糕的是……它可能在某些时候工作,而在其他时候不工作……)
所以我想知道,有没有一种便携的方法可以保存这些数据,而不只是将其保存为原始文本?
正如其他人所说:序列化可能是解决问题的最佳方案。
由于您处于资源受限的环境中,我建议您使用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);
}
作为替代方案,您可以查看谷歌的FlatBuffers。它看起来资源效率很高,但我还没有尝试过。
EDIT:下面是一个完整的示例,说明整个序列化-文件I/O-反序列化循环:
// 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),那么你很幸运,你可以使用基本的位操作来做到这一点。
您需要使用掩码逐字节设置缓冲区。
唯一需要做的不同的事情取决于机器的字节序。
不要重新发明轮子。这正是谷歌协议缓冲区旨在解决的问题——以一种不需要人类可读的方式在计算机之间传输定义良好的数据。(EG与JSON或XML相反)
或者,你可以去真正的老skool,在ASN.1 上阅读
为了完整起见,这里是数据序列化格式的比较,所以可以随意选择毒药。
最好的方法是使用序列化(ProtoBuf、Thrift等)。但是,如果你不能使用它,并且需要"原始"解决方案,唯一的方法就是在所有平台上使用相同大小的特殊类型来描述你的结构:
struct dataStruct{
uint32_t a; // see cstdint.h or boost
/// ...
}
您也需要小心字节顺序。因此,无论何时序列化(将其传递到"另一端"或保存到文件中),都必须始终将所有字段转换为小端序(或大端序)。
还有一件事需要记住的是结构包装)参见
#pragma pack(1)
或
__attribute__((packed))
这是一个广泛的主题,所以最简单的解决方案是使用序列化程序。
我会告诉您文本是最安全的。将其保存为原始int和doubles会导致big/little endian问题,并可能导致双布局格式的问题。如果不将不同的值相互分隔,即使是转到文本也可能会引起问题。
另一种选择是定义自己的"通用"格式,并在写/读操作中转换为该格式。。。也许输出int作为文本,但作为伪科学表示法文本,比如5个字符的尾数值、"e"和2/3个字符的指数。
- 是否有一种非间接、非黑客的方式来保证 constexpr 函数仅在编译时可调用?
- 是否有一种可接受的运行线程或按顺序执行的方法
- 在 C++14 中,是否有一种优雅的解决方案可以在可变参数模板中选择可调用和不可调用的类型
- 模板结构可容纳两种类型,但当时只有一种
- 有没有一种方法可以使全局函数/静态成员函数一次可呼出
- 是一种基本类型的挥发性初始化,可观察到的行为
- 一种在编译时用C++根据printf格式检查参数的可移植方法
- OpenGL:混叠是一种可预测且一致的现象吗
- 琐碎的可复制性总是一种伪标准布局
- 试图找出一种可移植的数据保存方法
- 有没有一种方法可以将编译器防火墙(Pimpl)和默认可复制性的优点结合起来
- 有没有一种方法可以为std::map中小于给定键的第一个元素找到反向迭代器
- 我正在寻找一种在从模型中删除并重新添加修改后的可提取对象后操作 iloextract 对象的方法
- 有没有一种可移植的方法可以知道uintptr_t是否在stdint.h中定义
- 有人能推荐一本关于可移植C/C++代码开发的书吗
- 有没有一种方法可以为std::array创建函数模板专用化
- 在c++ 11中,是否有一种方法可以在调用以任何可调用对象(包括绑定方法)作为参数的函数时不需要模板参数
- 是否有一种可编程的方法来估计CPU执行fp操作所需的时间
- c++中是否有一种可移植的方法将stdin重定向为string ?
- Boost::serialization——是否有一种可移植的方法来对std::wstring进行二进制序列化?