简体   繁体   English

读写C ++向量到文件

[英]Reading and writing C++ vector to a file

For some graphics work I need to read in a large amount of data as quickly as possible and would ideally like to directly read and write the data structures to disk. 对于某些图形工作,我需要尽快读取大量数据,理想情况下是直接读取和写入数据结构到磁盘。 Basically I have a load of 3d models in various file formats which take too long to load so I want to write them out in their "prepared" format as a cache that will load much faster on subsequent runs of the program. 基本上我有各种各样的文件格式的3D模型,加载时间太长,所以我想把它们以“准备好”的格式写出来作为缓存,在后续的程序运行中加载速度要快得多。

Is it safe to do it like this? 这样做是否安全? My worries are around directly reading into the data of the vector? 我担心的是直接读入载体的数据? I've removed error checking, hard coded 4 as the size of the int and so on so that i can give a short working example, I know it's bad code, my question really is if it is safe in c++ to read a whole array of structures directly into a vector like this? 我已经删除了错误检查,硬编码4作为int的大小等等,所以我可以举一个简短的工作示例,我知道这是错误的代码,我的问题是,如果在c ++中读取整个数组是安全的结构直接进入这样的矢量? I believe it to be so, but c++ has so many traps and undefined behavour when you start going low level and dealing directly with raw memory like this. 我相信它是这样的,但是当你开始进入低级并直接处理像这样的原始内存时,c ++有很多陷阱和未定义的行为。

I realise that number formats and sizes may change across platforms and compilers but this will only even be read and written by the same compiler program to cache data that may be needed on a later run of the same program. 我意识到数字格式和大小可能会在平台和编译器之间发生变化,但这只会由同一个编译器程序读取和写入,以缓存稍后运行同一程序时可能需要的数据。

#include <fstream>
#include <vector>

using namespace std;

struct Vertex
{
    float x, y, z;
};

typedef vector<Vertex> VertexList;

int main()
{
    // Create a list for testing
    VertexList list;
    Vertex v1 = {1.0f, 2.0f,   3.0f}; list.push_back(v1);
    Vertex v2 = {2.0f, 100.0f, 3.0f}; list.push_back(v2);
    Vertex v3 = {3.0f, 200.0f, 3.0f}; list.push_back(v3);
    Vertex v4 = {4.0f, 300.0f, 3.0f}; list.push_back(v4);

    // Write out a list to a disk file
    ofstream os ("data.dat", ios::binary);

    int size1 = list.size();
    os.write((const char*)&size1, 4);
    os.write((const char*)&list[0], size1 * sizeof(Vertex));
    os.close();


    // Read it back in
    VertexList list2;

    ifstream is("data.dat", ios::binary);
    int size2;
    is.read((char*)&size2, 4);
    list2.resize(size2);

     // Is it safe to read a whole array of structures directly into the vector?
    is.read((char*)&list2[0], size2 * sizeof(Vertex));

}

As Laurynas says, std::vector is guaranteed to be contiguous, so that should work, but it is potentially non-portable. 正如Laurynas所说, std::vector保证是连续的,所以应该可以工作,但它可能是不可移植的。

On most systems, sizeof(Vertex) will be 12, but it's not uncommon for the struct to be padded, so that sizeof(Vertex) == 16 . 在大多数系统中, sizeof(Vertex)将为12,但结构填充并不罕见,因此sizeof(Vertex) == 16 If you were to write the data on one system and then read that file in on another, there's no guarantee that it will work correctly. 如果您要在一个系统上写入数据然后在另一个系统上读取该文件,则无法保证它能正常工作。

You might be interested in the Boost.Serialization library. 您可能对Boost.Serialization库感兴趣。 It knows how to save/load STL containers to/from disk, among other things. 它知道如何在磁盘上保存/加载STL容器等等。 It might be overkill for your simple example, but it might become more useful if you do other types of serialization in your program. 对于您的简单示例而言可能有点过分,但如果您在程序中执行其他类型的序列化,它可能会变得更有用。

Here's some sample code that does what you're looking for: 以下是一些示例代码,可以满足您的需求:

#include <algorithm>
#include <fstream>
#include <vector>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>

using namespace std;

struct Vertex
{
    float x, y, z;
};

bool operator==(const Vertex& lhs, const Vertex& rhs)
{
    return lhs.x==rhs.x && lhs.y==rhs.y && lhs.z==rhs.z;
}

namespace boost { namespace serialization {
    template<class Archive>
    void serialize(Archive & ar, Vertex& v, const unsigned int version)
    {
        ar & v.x; ar & v.y; ar & v.z;
    }
} }

typedef vector<Vertex> VertexList;

int main()
{
    // Create a list for testing
    const Vertex v[] = {
        {1.0f, 2.0f,   3.0f},
        {2.0f, 100.0f, 3.0f},
        {3.0f, 200.0f, 3.0f},
        {4.0f, 300.0f, 3.0f}
    };
    VertexList list(v, v + (sizeof(v) / sizeof(v[0])));

    // Write out a list to a disk file
    {
        ofstream os("data.dat", ios::binary);
        boost::archive::binary_oarchive oar(os);
        oar << list;
    }

    // Read it back in
    VertexList list2;

    {
        ifstream is("data.dat", ios::binary);
        boost::archive::binary_iarchive iar(is);
        iar >> list2;
    }

    // Check if vertex lists are equal
    assert(list == list2);

    return 0;
}

Note that I had to implement a serialize function for your Vertex in the boost::serialization namespace. 请注意,我必须在boost::serialization命名空间中为Vertex实现serialize函数。 This lets the serialization library know how to serialize Vertex members. 这使序列化库知道如何序列化Vertex成员。

I've browsed through the boost::binary_oarchive source code and it seems that it reads/writes the raw vector array data directly from/to the stream buffer. 我浏览了boost::binary_oarchive源代码,它似乎直接从/向流缓冲区读取/写入原始向量数组数据。 So it should be pretty fast. 所以应该很快。

std::vector保证在内存中是连续的,所以,是的。

I just ran into this exact same problem. 我刚遇到了同样的问题。

First off, these statements are broken 首先,这些陈述被打破

os.write((const char*)&list[0], size1 * sizeof(Vertex));
is.read((char*)&list2[0], size2 * sizeof(Vertex));

There is other stuff in the Vector data structure, so this will make your new vector get filled up with garbage. Vector数据结构中还有其他内容,因此这将使您的新向量充满垃圾。

Solution: 解:
When you are writing your vector into a file, don't worry about the size your Vertex class, just directly write the entire vector into memory. 当您将矢量写入文件时,不要担心Vertex类的大小,只需将整个矢量直接写入内存即可。

os.write((const char*)&list, sizeof(list));

And then you can read the entire vector into memory at once 然后您可以立即将整个矢量读入内存

is.seekg(0,ifstream::end);
long size2 = is.tellg();
is.seekg(0,ifstream::beg);
list2.resize(size2);
is.read((char*)&list2, size2);

Another alternative to explicitly reading and writing your vector<> from and to a file is to replace the underlying allocator with one that allocates memory from a memory mapped file. 从文件中显式读取和写入vector<>另一种方法是将底层分配器替换为从内存映射文件中分配内存的分配器。 This would allow you to avoid an intermediate read/write related copy. 这将允许您避免中间读/写相关副本。 However, this approach does have some overhead. 但是,这种方法确实有一些开销。 Unless your file is very large it may not make sense for your particular case. 除非您的文件非常大,否则对您的特定情况可能没有意义。 Profile as usual to determine if this approach is a good fit. 像往常一样描述确定这种方法是否合适。

There are also some caveats to this approach that are handled very well by the Boost.Interprocess library. 这种方法也有一些注意事项, Boost.Interprocess库可以很好地处理这些注意事项。 Of particular interest to you may be its allocators and containers . 您特别感兴趣的可能是它的分配器和容器

If this is used for caching by the same code, I don't see any problem with this. 如果这用于通过相同的代码进行缓存,我认为没有任何问题。 I've used this same technique on multiple systems without a problem (all Unix based). 我在多个系统上使用了相同的技术而没有任何问题(所有基于Unix的)。 As an extra precaution, you might want to write a struct with known values at the beginning of the file, and check that it reads ok. 作为额外的预防措施,您可能希望在文件的开头编写一个具有已知值的结构,并检查它是否正常。 You might also want to record the size of the struct in the file. 您可能还想在文件中记录结构的大小。 This will save a lot of debugging time in the future if the padding ever changes. 如果填充变化,这将在未来节省大量的调试时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM