2012-12-30 5 views
12

grazie a Mats Petersson per la spiegazione di copiare il vettore in serie sembra funzionare. qui il codice snipset:come scrivere correttamente il vettore sul file binario in C++?

#include <iostream> 
#include <string.h> 
#include <vector> 
#include <fstream> 

using namespace std; 

class Student 
    { 
    private: 
    char m_name[30]; 
    int m_score; 

    public: 
    Student() 
     { 

     } 
    Student(const Student& copy) 
     { 
      m_score = copy.m_score; //wonder why i can use this statment as 
      strncpy(m_name, copy.m_name, 30); //declare it private 
     } 
     Student(const char name[], const int &score) 
     :m_score(score) 
     { 
      strncpy(m_name, name, 30); 
     } 
     void print() const 
     { 
      cout.setf(ios::left); 
      cout.width(20); 
      cout << m_name << " " << m_score << endl; 
     } 
     }; 


     int main() 
     { 
     vector<Student> student; 
     student.push_back(Student("Alex",19)); 
     student.push_back(Student("Maria",20)); 
     student.push_back(Student("muhamed",20)); 
     student.push_back(Student("Jeniffer",20)); 
     student.push_back(Student("Alex",20)); 
     student.push_back(Student("Maria",21)); 
     { 
     Student temp[student.size()]; 
     unsigned int counter; 
     for(counter = 0; counter < student.size(); ++counter) 
     { 
     temp[counter] = student[counter]; 
     } 

     ofstream fout("data.dat", ios::out | ios::binary); 
     fout.write((char*) &temp, sizeof(temp)); 
     fout.close(); 
     } 

     vector<Student> student2; 
     ifstream fin("data.dat", ios::in | ios::binary); 

     { 
     fin.seekg(0, ifstream::end); 
     int size = fin.tellg()/sizeof (Student); 
     Student temp2[size]; 
     fin.seekg(0, ifstream::beg); 
     fin.read((char*)&temp2, sizeof(temp2)); 
     int counter; 
     for(counter = 0; counter <6; ++counter) 
     { 
     student2.push_back(temp2[counter]); 
     } 
     fin.close(); 
     } 
     vector<Student>::iterator itr = student2.begin(); 
     while(itr != student2.end()) 
     { 
     itr->print(); 
     ++itr; 
     } 
     return 0; 
     } 

ma io ospite questo metodo sprecherà enorme memoria e ingombro. forse prenderò in considerazione di scriverlo Mr. con ocelot e altri suggerimenti. ringrazia tutti per la risposta.

+3

Suggerimenti standard: compila con 'g ++ -Wall -g', migliora il tuo codice fino a quando non vengono forniti avvisi, impara a usare' gdb' e 'valgrind' per eseguirne il debug. –

+0

beh, non mi sono ancora abituato alla tecnica di compilazione dei terminali. Immagino di doverlo imparare in qualche modo. Grazie per la risposta. – dchochan

+0

Quindi, impara anche a usare 'make' e scrivi il tuo semplice' Makefile' –

risposta

8

Si sta scrivendo per archiviare la struttura vettoriale, non il suo buffer di dati. Provare a cambiare la scrittura procedura:

ofstream fout("data.dat", ios::out | ios::binary); 
fout.write((char*)&student[0], student.size() * sizeof(Student)); 
fout.close(); 

E invece di dimensioni calcolo del vettore dalle dimensioni del file, è meglio dimensione del vettore di scrittura (numero di oggetti) prima. Nel caso in cui si possano scrivere sullo stesso file altri dati.

size_t size = student.size(); 
fout.write((char*)&size, sizeof(size)); 
+0

Intendevi inserire 'sizeof (size)' nell'ultima riga? –

3

Probabilmente non è possibile scrivere in binario (come si sta facendo) qualsiasi std::vector perché quel modello contiene puntatori interni e la scrittura e la rilettura non ha senso.

Alcuni consigli generali:

  • non scrivere in binario eventuali contenitori di modello STL (come std::vector o std::map), che sicuramente contengono puntatori interni che davvero non si desidera scrivere come è. Se hai davvero bisogno di scriverli, implementa le tue routine di scrittura e lettura (ad esempio usando gli iteratori STL).

  • evitare di utilizzare strcpy senza cura. Il tuo codice andrà in crash se il nome ha più di 30 caratteri. Almeno, usa strncpy(m_name, name, sizeof(m_name)); (ma anche quello funzionerebbe male per un nome di 30 caratteri). In realtà, m_name dovrebbe essere un std::string.

  • serializza esplicitamente le classi del contenitore (gestendo ogni dato membro significativo). Potresti prendere in considerazione l'uso della notazione JSON (o forse YAML, o forse anche XML - che trovo troppo complesso quindi sconsigliamo) di serializzare. Ti dà un formato di dump testuale, che puoi facilmente ispezionare con un editor standard (ad esempio emacs o gedit). Troverai molte serializzazione di librerie libere, ad es. jsoncpp e molti altri.

  • imparare a compilare con g++ -Wall -g e utilizzare il debugger gdb e il rilevatore di dispersioni valgrind memoria; Impara anche a usare make e scrivere il tuo Makefile-s.

  • sfruttare Linux è un software gratuito, quindi è possibile esaminare il suo codice sorgente (e si potrebbe voler studiare l'implementazione di stdC++ anche se le intestazioni STL sono complesse).

+0

ok grazie per la risposta, ma posso chiederti ancora una volta. avevo provato questo codice in Windows a volte funzionava e a volte lo schiacciavo. – dchochan

+0

! E allora? Il tuo programma ha ** comportamento non definito **. –

+0

Immagino che tu abbia ragione, forse la gestione e la sicurezza della memoria di wind * ws non sono così strette come Linux. – dchochan

2

Per le funzioni read() e write(), è necessario quello che viene chiamato "vecchi dati plain" o "POD". Ciò significa fondamentalmente che la classe o la struttura non deve avere puntatori al loro interno e nessuna funzione virtuale. l'implementazione del vettore ha certamente dei puntatori: non sono sicuro delle funzioni virtuali.

Dovrai scrivere una funzione che memorizzi uno studente alla volta (o che traduce un gruppo di studenti in un array [non un vettore] di byte o alcuni di essi - ma è più complesso).

Il motivo per cui non è possibile scrivere i dati non POD, in particolare i puntatori, in un file binario è che quando si leggono di nuovo i dati, si può quasi certamente scommettere che il layout della memoria è cambiato da quando lo si è scritto . Diventa un po 'come cercare di parcheggiare nello stesso parcheggio nei negozi - qualcun altro avrà parcheggiato al terzo posto dall'ingresso quando si presenterà la prossima volta, quindi dovrai scegliere un altro posto. Pensa alla memoria allocata dal compilatore come spazi di parcheggio e alle informazioni degli studenti come automobili.

[Tecnicamente, in questo caso, è anche peggio: il vettore in realtà non contiene gli studenti all'interno della classe, che è ciò che stai scrivendo sul file, quindi non hai nemmeno salvato le informazioni sugli studenti , solo le informazioni su dove si trovano (il numero dei parcheggi)]

14

Per memorizzare un vector<T>di PODs in un file, è necessario scrivere il contenuto del vettore, non il vettore stesso. È possibile accedere ai dati non elaborati con &vector[0], indirizzo del primo elemento (dato che contiene almeno un elemento). Per ottenere la lunghezza dei dati grezzi, moltiplicare il numero di elementi nel vettore con le dimensioni di un elemento:

strm.write(reinterpret_cast<const char*>(&vec[0]), vec.size()*sizeof(T)); 

Lo stesso vale quando si legge il vettore dal file; Il conteggio elemento è la dimensione del file totale diviso per le dimensioni di un elemento (dato che si memorizza un solo tipo di POD nel file):

const size_t count = filesize/sizeof(T); 
std::vector<T> vec(count); 
strm.read(reinterpret_cast<char*>(&vec[0]), count*sizeof(T)); 

Questo funziona solo se è possibile calcolare il numero di elementi di base sulla dimensione del file (se si memorizza solo un tipo di POD o se tutti i vettori contengono lo stesso numero di elementi). Se si dispone di vettori con diversi POD con lunghezze diverse, è necessario scrivere il numero di elementi nel vettore sul file prima di scrivere i dati grezzi.

Inoltre, quando si trasferiscono tipi numerici in formato binario tra sistemi diversi, prestare attenzione a endianness.