4

我有一个在英特尔 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 数组写入文件。然而,一点点阅读告诉我,这样的实现可能不一定在操作系统之间兼容(更糟糕的是......它可能在某些时候工作而不是其他时候......)。

所以我想知道 - 是否有一种可移植的方式来保存这些数据而不仅仅是将其保存为原始文本?

4

5 回答 5

2

正如其他人所说:序列化可能是您问题的最佳解决方案。

由于您处于资源受限的环境中,因此我建议使用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。它似乎非常节省资源,但我还没有尝试过。

编辑:这是一个完整的示例,说明了整个序列化 - 文件 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]
于 2015-12-17T21:24:56.783 回答
1

不要重新发明轮子。这就是 Google Protocol Buffers旨在解决的问题 - 以不需要人类可读的方式在计算机之间传输定义明确的数据。(EG 相对于 JSON 或 XML)

或者,您可以去真正的老学校并阅读ASN.1

为了完整起见,这里是数据序列化格式的比较,所以随意挑选你的毒药。

于 2015-12-17T20:28:33.430 回答
1

您需要序列化您的数据。而且由于我可以假设 boost 不是一种选择,因此您必须手动进行。

真正的可移植性(期望未签名)令人头疼。但是,如果您知道您使用的所有系统都使用相同的编码(例如,有符号整数的二进制补码和浮点的 IEE754),那么您很幸运,您可以使用基本的位操作来做到这一点。

您需要使用掩码逐字节设置缓冲区。

您唯一需要做的事情取决于机器的字节字节序。

于 2015-12-17T20:05:51.703 回答
0

最好的方法是使用序列化(ProtoBuf、Thrift 等)。但是,如果您不能使用它并且需要“原始”解决方案,那么唯一的方法是使用在所有平台上具有相同大小的特殊类型来描述您的结构:

struct dataStruct{
  uint32_t a;  // see cstdint.h or boost
  /// ...
}

您也需要注意字节顺序。因此,无论何时序列化(将其传递到“另一端”或保存到文件),您必须始终将所有字段转换为小端(或大端)。

要记住的另一件事是结构包装))见

#pragma pack(1)

或者

__attribute__((packed))

这是一个广泛的话题,所以最简单的解决方案是使用序列化程序。

于 2015-12-17T20:41:35.150 回答
-1

我会告诉你,文字是最安全的。将其保存为原始 int 和双打会使您面临大/小端问题,并可能会遇到双重布局格式的问题。如果您不将各种值相互分隔,即使是文本也可能会导致问题。

另一种方法是定义您自己的“通用”格式并在您的写入/读取操作中转换为/从该格式转换......也许将 int 输出为文本,但作为伪科学记数法文本说一个 5 个字符的尾数值,一个“e”和一个 2/3 字符的指数。

于 2015-12-17T20:31:42.653 回答