c++ file-io vector

Leyendo y escribiendo vector C++ a un archivo.



vector stl c++ reference (6)

Para algunos trabajos gráficos, necesito leer una gran cantidad de datos lo más rápido posible, y lo ideal sería leer y escribir directamente las estructuras de datos en el disco. Básicamente tengo una carga de modelos 3D en varios formatos de archivo que tardan demasiado en cargarse, así que quiero escribirlos en su formato "preparado" como un caché que se cargará mucho más rápido en las siguientes ejecuciones del programa.

¿Es seguro hacerlo así? ¿Mis preocupaciones están alrededor de leer directamente en los datos del vector? He eliminado la comprobación de errores, el código 4 duro como el tamaño del int y así sucesivamente para poder dar un breve ejemplo de trabajo, sé que es un código incorrecto, mi pregunta realmente es si es seguro en c ++ leer una matriz completa de estructuras directamente en un vector como este? Creo que es así, pero c ++ tiene tantas trampas y comportamiento indefinido cuando empiezas a pasar de nivel bajo y tratas directamente con memoria en bruto como esta.

Me doy cuenta de que los formatos y tamaños de los números pueden cambiar a través de las plataformas y los compiladores, pero esto solo será leído y escrito por el mismo programa del compilador para almacenar en caché los datos que pueden ser necesarios en una ejecución posterior del mismo programa.

#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)); }


Acabo de encontrar exactamente el mismo problema.

En primer lugar, estas declaraciones están rotas

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

Hay otras cosas en la estructura de datos de vectores, por lo que esto hará que su nuevo vector se llene de basura.

Solución:
Cuando esté escribiendo su vector en un archivo, no se preocupe por el tamaño de su clase Vertex, simplemente escriba el vector completo en la memoria.

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

Y luego puedes leer todo el vector en la memoria a la vez

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


Como dice Laurynas, se garantiza que std::vector sea ​​contiguo, por lo que debería funcionar, pero es potencialmente no portátil.

En la mayoría de los sistemas, sizeof(Vertex) será 12, pero no es raro que la estructura se rellene, por lo que sizeof(Vertex) == 16 . Si tuviera que escribir los datos en un sistema y luego leer ese archivo en otro, no hay garantía de que funcionará correctamente.


Es posible que esté interesado en la biblioteca Boost.Serialization . Sabe cómo guardar / cargar contenedores STL en / desde el disco, entre otras cosas. Podría ser una exageración para su ejemplo simple, pero podría ser más útil si realiza otros tipos de serialización en su programa.

Aquí hay un código de ejemplo que hace lo que estás buscando:

#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; }

Tenga en cuenta que tuve que implementar una función de serialize para su Vertex en el espacio de nombres boost::serialization . Esto permite a la biblioteca de serialización saber cómo serializar los miembros de Vertex .

He boost::binary_oarchive código fuente boost::binary_oarchive y parece que lee / escribe los datos de la matriz de vector sin procesar directamente desde / al búfer de flujo. Así que debería ser bastante rápido.


Otra alternativa para leer y escribir explícitamente su vector<> desde y hacia un archivo es reemplazar el asignador subyacente por uno que asigna memoria desde un archivo asignado en memoria. Esto le permitiría evitar una copia intermedia relacionada con lectura / escritura. Sin embargo, este enfoque tiene algunos gastos generales. A menos que su archivo sea muy grande, puede que no tenga sentido para su caso particular. Perfil como de costumbre para determinar si este enfoque es un buen ajuste.

También hay algunas advertencias a este enfoque que son manejadas muy bien por la biblioteca Boost.Interprocess . De particular interés para usted pueden ser sus asignadores y contenedores .


Si esto se usa para el almacenamiento en caché por el mismo código, no veo ningún problema con esto. He usado esta misma técnica en varios sistemas sin problemas (todos basados ​​en Unix). Como precaución adicional, es posible que desee escribir una estructura con valores conocidos al principio del archivo y verificar que se lea bien. Es posible que también desee registrar el tamaño de la estructura en el archivo. Esto ahorrará mucho tiempo de depuración en el futuro si el relleno cambia.


std::vector está garantizado para ser continuo en la memoria, entonces, sí.