试图找出一种便携式数据保存方法
Trying to figure out a portable data saving approach
我有一个程序 运行 在英特尔 Edison(32 位 Yocto Linux)上。它读取传感器数据,然后将该传感器数据写入文件。数据以 1 int 和 13 double 的数据包形式出现,每秒有 100 个数据包到达。一段时间后,我将从中提取文件并在 x64 windows 机器上使用工具 运行 读取这些文件。
目前我正在将数据作为原始文本文件写入(因为字符串很好且可移植)。但是,由于为此写入的数据量很大,我正在寻找保存方法 space。但是,我正在尝试找出一种方法,以便在另一方对此进行解释时不会丢失任何数据。
我最初的想法是继续创建一个如下所示的结构:
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数组写入文件。然而,一些阅读告诉我,这样的实现可能不一定在 OS 之间兼容(更糟糕的是......它可能在某些时候工作而不是其他时候......)。
所以我想知道 - 是否有一种可移植的方式来保存这些数据而不仅仅是将其保存为原始文本?
您需要序列化您的数据。由于我可以假设提升不是一种选择,因此您必须手动完成。
真正的可移植性(期望未签名)令人头疼。但是,如果您知道您使用的所有系统都使用相同的编码(例如,有符号整数的二进制补码和浮点的 IEE754),那么您很幸运,您可以使用基本的位操作来做到这一点。
您需要使用掩码逐字节设置缓冲区。
您唯一需要做的不同的事情取决于机器的字节顺序。
不要重新发明轮子。这就是 Google Protocol Buffers 旨在解决的问题 - 以不需要人类可读的方式在计算机之间传输明确定义的数据。 (EG 而不是 JSON 或 XML)
或者你可以去真正的老学校继续阅读 ASN.1
为了完整起见,这里有一个Comparison of data serialization formats,所以请随意挑选你的毒药。
我会告诉你,文字是最安全的。将其保存为原始 int 和双打会使您面临 big/little 字节顺序问题,并且可能会遇到双布局格式的问题。如果您不将各种值彼此分隔开,即使是文本也可能会导致问题。
另一种方法是定义您自己的 "universal" 格式并在您的 write/read 操作中翻译 to/from...也许输出 int 作为文本但作为伪科学记数法文本说一个 5 个字符的尾数值,一个 'e' 和一个 2/3 字符的指数。
最好的方法是使用序列化(ProtoBuf、Thrift 等)。但是如果你不能使用它并且需要 'raw' 解决方案,唯一的方法是使用在所有平台上具有相同大小的特殊类型来描述你的结构:
struct dataStruct{
uint32_t a; // see cstdint.h or boost
/// ...
}
您也需要注意字节顺序。因此,无论何时对其进行序列化(将其传递到 "other" 端或保存到文件),都必须始终将所有字段转换为小端(或大端)。
还要记住的一件事是结构打包 )) 请参阅
#pragma pack(1)
或
__attribute__((packed))
这是一个广泛的话题,所以最简单的解决方案是使用序列化程序。
正如其他人所说:序列化可能是您问题的最佳解决方案。
由于您处于资源受限的环境中,我建议使用 MsgPack 之类的东西。它只有头文件 (给定一个 C++11 编译器),非常轻巧,格式简单,C++ 接口很好。它甚至允许您非常轻松地序列化用户定义的类型(即 classes/structs):
// 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。好像挺省资源的,不过我还没试过
编辑:这是说明整个序列化的完整示例-文件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]
我有一个程序 运行 在英特尔 Edison(32 位 Yocto Linux)上。它读取传感器数据,然后将该传感器数据写入文件。数据以 1 int 和 13 double 的数据包形式出现,每秒有 100 个数据包到达。一段时间后,我将从中提取文件并在 x64 windows 机器上使用工具 运行 读取这些文件。
目前我正在将数据作为原始文本文件写入(因为字符串很好且可移植)。但是,由于为此写入的数据量很大,我正在寻找保存方法 space。但是,我正在尝试找出一种方法,以便在另一方对此进行解释时不会丢失任何数据。
我最初的想法是继续创建一个如下所示的结构:
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数组写入文件。然而,一些阅读告诉我,这样的实现可能不一定在 OS 之间兼容(更糟糕的是......它可能在某些时候工作而不是其他时候......)。
所以我想知道 - 是否有一种可移植的方式来保存这些数据而不仅仅是将其保存为原始文本?
您需要序列化您的数据。由于我可以假设提升不是一种选择,因此您必须手动完成。
真正的可移植性(期望未签名)令人头疼。但是,如果您知道您使用的所有系统都使用相同的编码(例如,有符号整数的二进制补码和浮点的 IEE754),那么您很幸运,您可以使用基本的位操作来做到这一点。
您需要使用掩码逐字节设置缓冲区。
您唯一需要做的不同的事情取决于机器的字节顺序。
不要重新发明轮子。这就是 Google Protocol Buffers 旨在解决的问题 - 以不需要人类可读的方式在计算机之间传输明确定义的数据。 (EG 而不是 JSON 或 XML)
或者你可以去真正的老学校继续阅读 ASN.1
为了完整起见,这里有一个Comparison of data serialization formats,所以请随意挑选你的毒药。
我会告诉你,文字是最安全的。将其保存为原始 int 和双打会使您面临 big/little 字节顺序问题,并且可能会遇到双布局格式的问题。如果您不将各种值彼此分隔开,即使是文本也可能会导致问题。
另一种方法是定义您自己的 "universal" 格式并在您的 write/read 操作中翻译 to/from...也许输出 int 作为文本但作为伪科学记数法文本说一个 5 个字符的尾数值,一个 'e' 和一个 2/3 字符的指数。
最好的方法是使用序列化(ProtoBuf、Thrift 等)。但是如果你不能使用它并且需要 'raw' 解决方案,唯一的方法是使用在所有平台上具有相同大小的特殊类型来描述你的结构:
struct dataStruct{
uint32_t a; // see cstdint.h or boost
/// ...
}
您也需要注意字节顺序。因此,无论何时对其进行序列化(将其传递到 "other" 端或保存到文件),都必须始终将所有字段转换为小端(或大端)。
还要记住的一件事是结构打包 )) 请参阅
#pragma pack(1)
或
__attribute__((packed))
这是一个广泛的话题,所以最简单的解决方案是使用序列化程序。
正如其他人所说:序列化可能是您问题的最佳解决方案。
由于您处于资源受限的环境中,我建议使用 MsgPack 之类的东西。它只有头文件 (给定一个 C++11 编译器),非常轻巧,格式简单,C++ 接口很好。它甚至允许您非常轻松地序列化用户定义的类型(即 classes/structs):
// 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。好像挺省资源的,不过我还没试过
编辑:这是说明整个序列化的完整示例-文件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]