2010-08-09 9 views
4

J'ai des problèmes à essayer de sérialiser un vecteur (std :: vector) dans un format binaire, puis de le désérialiser correctement et de pouvoir lire les données. C'est la première fois que j'utilise un format binaire (j'utilisais l'ASCII mais c'est devenu trop dur à utiliser maintenant) donc je commence simple avec juste un vecteur d'ints.Sérialiser et désérialiser un vecteur en binaire

Chaque fois que je relis les données, le vecteur a toujours la bonne longueur mais les données sont 0, indéfinies ou aléatoires.

class Example 
{ 
public: 
    std::vector<int> val; 
}; 

ECRITURE:

Example example = Example(); 
example.val.push_back(10); 
size_t size = sizeof BinaryExample + (sizeof(int) * example.val.size()); 

std::fstream file ("Levels/example.sld", std::ios::out | std::ios::binary); 

if (file.is_open()) 
{ 
    file.seekg(0); 
    file.write((char*)&example, size); 
    file.close(); 
} 

LIRE:

BinaryExample example = BinaryExample(); 

std::ifstream::pos_type size; 
std::ifstream file ("Levels/example.sld", std::ios::in | std::ios::binary | std::ios::ate); 

if (file.is_open()) 
{ 
    size = file.tellg(); 

    file.seekg(0, std::ios::beg); 
    file.read((char*)&example, size); 
    file.close(); 
} 

Est-ce que quelqu'un sait ce que je fais mal ou quoi faire ou être en mesure de me diriger dans la direction que je dois faire?

+1

Bienvenue sur SO. Pour mettre en forme un bloc de code, indentez le code entier de 4 espaces. – kennytm

+0

Merci pour cela, le formatage me rendait fou. J'ai parlé à quelqu'un d'autre de ma question et il me semble que je dois faire plusieurs appels d'écriture pour chaque élément du vecteur ainsi que la taille du vecteur. –

+0

Vous pouvez passer du temps à lire http://stackoverflow.com/editing-help. – kennytm

Répondre

5

Vous ne pouvez pas désintégrer une classe non-POD en écrasant une instance existante comme vous semblez le faire - vous devez donner à la classe un constructeur qui lit les données du flux et construit une nouvelle instance du classe avec elle.

Dans les grandes lignes, donné quelque chose comme ceci:

class A { 
    A(); 
    A(istream & is);  
    void serialise(ostream & os); 
    vector <int> v; 
}; 

serialise alors() écrirait la longueur du vecteur suivi par le contenu du vecteur. Le constructeur lirait la longueur du vecteur, redimensionner le vecteur en utilisant la longueur, puis lire le contenu du vecteur:

void A :: serialise(ostream & os) { 
    size_t vsize = v.size();  
    os.write((char*)&vsize, sizeof(vsize)); 
    os.write((char*)&v[0], vsize * sizeof(int)); 
} 

A :: A(istream & is) { 
    size_t vsize; 
    is.read((char*)&vsize, sizeof(vsize)); 
    v.resize(vsize); 
    is.read((char*)&v[0], vsize * sizeof(int)); 
} 
+0

@Neil: oops, j'ai ajouté une partie sur le format ASCII sur la mauvaise entrée. J'espère que cela ne vous dérange pas. Je vais supprimer ma propre réponse. – kriss

+0

@Neil: pour être plus précis, vous pouvez sérialiser (ou memcopy) directement uniquement des classes triviales. – kriss

+0

@kriss a ajouté une note à propos de non-POD pour répondre. –

2

Vous utilisez l'adresse du vecteur. Ce dont vous avez besoin est l'adresse des données détenues par le vecteur. L'écriture, par exemple, serait quelque chose comme:

size = example.size(); 
file.write((char *)&size, sizeof(size)); 
file.write((char *)&example[0], sizeof(example[0] * size)); 
2

Je voudrais écrire dans l'ordre des octets du réseau pour assurer que le fichier peut être écrit & lecture sur toute plate-forme. Donc:

#include <fstream> 
#include <iostream> 
#include <iomanip> 
#include <vector> 

#include <arpa/inet.h> 

int main(void) { 

    std::vector<int32_t> v = std::vector<int32_t>(); 
    v.push_back(111); 
    v.push_back(222); 
    v.push_back(333); 


    { 
    std::ofstream ofs; 
    ofs.open("vecdmp.bin", std::ios::out | std::ios::binary); 

    uint32_t sz = htonl(v.size()); 
    ofs.write((const char*)&sz, sizeof(uint32_t)); 
    for (uint32_t i = 0, end_i = v.size(); i < end_i; ++i) { 
     int32_t val = htonl(v[i]); 
     ofs.write((const char*)&val, sizeof(int32_t)); 
    } 

    ofs.close(); 
    } 

    { 
    std::ifstream ifs; 
    ifs.open("vecdmp.bin", std::ios::in | std::ios::binary); 

    uint32_t sz = 0; 
    ifs.read((char*)&sz, sizeof(uint32_t)); 
    sz = ntohl(sz); 

    for (uint32_t i = 0; i < sz; ++i) { 
     int32_t val = 0; 
     ifs.read((char*)&val, sizeof(int32_t)); 
     val = ntohl(val); 
     std::cout << i << '=' << val << '\n'; 
    } 
    } 

    return 0; 
} 
1

Lisez la réponse de l'autre pour voir comment vous devriez lire/écrire une structure binaire.

J'ajoute celui-ci parce que je crois que vos motivations pour utiliser un format binaire sont erronées. Un format binaire ne sera pas plus facile qu'un format ASCII, d'habitude c'est l'inverse.

Vous disposez de nombreuses options pour enregistrer/lire des données à des fins d'utilisation à long terme (ORM, bases de données, formats structurés, fichiers de configuration, etc.). Le fichier binaire plat est généralement le pire et le plus difficile à maintenir, sauf pour les structures très simples.

+0

La façon dont j'utilisais l'ASCII était facile, mais pas très modulaire ou extensible. Je n'ai pas encore fait de fichiers binaires, alors j'ai pensé que j'allais essayer, merci pour le commentaire. –

Questions connexes