【问题标题】:Storing, retrieving binary data or bytes to and from a file for a std::deque?在 std::deque 的文件中存储、检索二进制数据或字节?
【发布时间】:2013-02-27 21:19:15
【问题描述】:

我正在为一个游戏编写一个“回放系统”,我想知道我应该如何存储记录的帧?

至于现在我有这个代码/结构(注意:这个代码被缩短了):

struct Point4D { float x, y, z, w; };
struct Point3D { float x, y, z; };
struct Point2D { float x, y; };
struct QuatRot { Point4D Front,Right,Up; };
struct VehicleInfo
{
    Point3D     Pos,Velocity,TurnSpeed;
    QuatRot     Rotation;
};
namespace Recorder
{
    struct FrameInfo
    //~380 bytes / frame| max 45600 bytes @ 120 fps
    //max 3.7 GB raw data for 24 hours of recording, not bad..
    {
        std::chrono::high_resolution_clock::duration time;
        VehicleInfo Vehicle;
        int Nitro;
        float RPM;
        int CurrentGear;
    };

    std::deque<FrameInfo> frames;
    FrameInfo g_Temp;

    void ProcessRecord()
    {
        //record vehicle data here to g_Temp
        frames.push_back(g_Temp);
        return;
    }
    //other recording code.......
};

我的想法是,制作一个原始字节数组,将其分配给双端队列容器的大小,用 memcpy 将它们从双端队列复制到数组中,然后将所有数组字节写入文件..

然后,如果我想读取我的记录数据,我只需读取文件的字节并将它们分配给一个新数组,然后使用 memcpy 将数组内容复制到一个双端队列..

这很像..好吧.. C方式? 必须有其他方法来做到这一点,将数据存储在文件中,然后将其读回双端队列(也许使用一些 C++11 功能?)。

我将如何做到这一点?

您推荐哪种方法?

如果这很重要,我正在使用 Windows。

【问题讨论】:

  • std::deque 不需要:1)将其内存存储在连续存储中,因此不会有“来自/到双端队列的memcpy”。 2) 将其内存存储在std::deque 对象中。所以你的计划是行不通的,期间。你为什么还要使用std::deque
  • 这种 memcpy 的事情甚至不可能使用 deque,因为在内部元素不一定存储在一个连续的数组中。
  • @NicolBolas 因为我需要在录制时 push_back 并在回放时获取 front() 和 pop_front()。
  • @GamErix:重播时为什么需要pop_front?只需推进一个迭代器;取出所有数据后,删除整个缓冲区。
  • 这是一个想法,但我将最后的记录存储到历史记录中,所以只是为了以不同的帧速率进行同步,我删除了“太旧”的帧。当它开始播放时,将来不再需要所有其他帧。所以我可以删除它.. ?

标签: c++ file memory-management c++11 deque


【解决方案1】:

memcpy 是过早优化。

从磁盘读取内容时,您的瓶颈是磁盘 IO,而不是将其从内存的一部分复制到另一部分。

修复您的数据结构,使其使用固定大小的数据结构(而不​​是 int,使用 32 位 int 等)。

不要用二进制写std::chrono::high_resolution_clock::duration——库更新可以完全改变它的大小,而不用眨眼或流泪,更不用说它的意义了。用ms 或其他东西写出来,所以意思总是保持不变,用(比如说)64 位整数。然后您可以将其读回您的std::chrono::high_resolution_clock::duration

序列化时总是写出版本号和结构大小,这样反序列化甚至可以处理基本的版本控制。

我会写一个“to stream”和“from stream”。 “to stream”写出版本号和大小。 “from stream”读取版本号和大小,加载当前版本和流版本中的每个字段,清除剩余的,然后从流中读取剩余的数据。

如果您发现自己需要更高的性能,您会注意到汽车的位置和角度会比齿轮更频繁地变化。此外,丢弃在现有帧之间合理插值的帧会大大减小重放格式的大小(根据您的描述,这不像是在重放中运行物理)。最后,如果你有一个一致的物理模型,那么只存储用户输入并基于它进行回放是可能的(但这很难实现)。

其他疯狂:只需在有问题的结构上调用 operator= 即可替换 SRECORDASSIGN。像0x4C 这样应用于指针的幻数很愚蠢,几乎总是可以用简单的结构成员访问来替换。

【讨论】:

  • operator = 不适用于我的数据,:Vehicles[id] = &amp;(*(structure*)address);,然后例如我必须做 Vehicles[id]-&gt;Pos.x = Source.Pos.x; 因为Vehicled[id] = Source 没有设置数据...车辆被声明为: VehicleInfo *Vehicles[MAX_VEHICLES];
  • 顺便说一句,这也可以这样看:Vehicles[id] = &amp;(*(structure*)address); // = &amp;POINTER(structure,address);
  • @GamErix *Vehicles[id] = Source 是您在这种情况下使用 operator= 的方式。指针和数据是不同的东西。
【解决方案2】:

如果我正确解释了您的问题(我很累,如果我错了,请留下评论),您想在文件中写入和读取您的录音。

这可以通过任何结构轻松完成:

struct Foo
{
   float bar;
   int baz;
};
std::ostream& operator<<(std::ostream& stream, const Foo &foo)
{
   stream << foo.bar << " " << foo.baz;
}
std::ofstream& operator<<(std::ofstream& stream, Foo &foo)
{
   stream.write(reinterpret_cast<char*>(&foo.bar), sizeof(bar));
   stream.write(reinterpret_cast<char*>(&foo.baz), sizeof(baz));
}
std::ifstream& operator>>(std::ifstream& stream, Foo &foo)
{
   stream.read(reinterpret_cast<char*>(&foo.bar), sizeof(bar));
   stream.read(reinterpret_cast<char*>(&foo.baz), sizeof(baz));
}

你可以用这个来测试

#include <fstream>
#include <iostream>

int main()
{
   Foo foo;
   foo.bar = -1.2f;
   foo.baz = 37;
    std::cout << foo << "\n";
   std::ofstream output;
   output.open("myfile", std::ios::binary);
   output << foo;
   output.close();
   std::ifstream input;
   input.open("myfile", std::ios::binary);
   input >> foo;
   input.close();
   std::cout << foo << "\n";
}

有关std::basic_ostream::writestd::basic::istream::read 的更多信息,我建议您查看cppreference.com

【讨论】:

  • 如何存储或检索二进制数据?
  • 如果我有一个像“2000000000”这样的4字节值,那么用这种方法不会将它存储为字符串吗?这将需要 10 个字节。我在 C++ 中找不到任何关于字节流或其他内容的内容
  • @GamErix:不,使用此方法将 4 个字节完全打印为 4 个字节。没有“字节流”,但有二进制模式的流,如上面的答案所示。我添加了一些链接作为对 readwrite 方法的参考。
  • 就是这样,那我想我可以在操作员&lt;&lt; 中做stream &lt;&lt; foo.bar &lt;&lt; foo.baz; 还是必须有空间?
  • @GamErix: 不。ostream::operator&lt;&lt; 的实现仅用于人类可读的形式,因此有更好的可读性空间(用于调试!)。只有ofstream::operator&lt;&lt; 的实现才会将二进制写入文件。如果您编译我的示例并打开文件myfile,您将看不到“-1.2f37”,请继续尝试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-19
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多