Serialisieren
-
Hallo
Zuerst mal wieder die Ankündigung, dass MFC für mich fast noch ein Buch mit sieben Siegeln ist.
Ich schreibe gearde eine kleine MFC-Anwendung und würde nungerne Daten speichern. Ich habe alle relevanten Daten in einer Klasse "gesammelt" und würde diese am liebsten komplett in eine Datei schreiben. Geht dies oder muss ich alle Membervaribelen einzeln speichern und wie verhält es sich mit eigenen Datentypen?
chrische
-
also im normalfall wenn du ein MFC Project vom wizard erstellen läßt wird ja auch eine Methode Serialize mitgeneriert.
Dem Archivstream ar in dem alles reingepackt wird musst du nur noch angeben was reinsoll.
also
// reinschreiben in den Stream ar << strukturxyz << membervariablexyz << feldxyz; //lesen aus dem stream ar >> strukturxyz >> membervariablexyz >> feldxyz;
zu beachten ist dabei das die Reihenfolge beim auslesen gleich bleibt, sonst wird der Stream falsch auseinandergenommen.
gruß
McSnoop
-
Es gibt verschiedene Wege, aber die zwei Hauptklassen zum Speichern von Daten per Serialisierung sind CFile und CArchive. Wenn du ein SDI oder ein MDI hast, dann gibt es in der CDocument-Klasse bereits eine Funktion Serialize, welche ein CArchive Objekt bekommt, in welches man dann die Daten speichern kann. In CArchive speichert oder lädt man Daten nacheinander, entweder wenn sie einen entsprechenden operator aufweisen mit den << oder >> Operatoren und ansonsten über eine eigene Funktion Serialize.
Mal ein Beispiel anhand einer eigenen Klasse:
class COwnClass { public: COwnClass() {}; virtual ~COwnClass() {}; private: int m_nInteger; CString m_strName; CTime m_Time; public: void Serialize(CArchive& ar); int& GetInteger() { return m_nInteger; }; CString& GetName() { return m_strName; }; CTime& GetTime() { return m_Time; }; } void COwnClass::Serialize(CArchive& ar) { if(ar.IsStoring()) { ar << m_nInteger << m_strName; } else { ar >> m_nInteger >> m_strName; } m_Time.Serialize64(ar); } // Irgendwo anderst void InAFunktion() { CFile File; if(File.Open(_T("C:\\TestFile.tf"), CFile::modeCreate | CFile::modeWrite)) { CArchive ar(&File, CArchive::store); // oder CArchive::load fürs laden, dann aber auch CFile::modeWrite ;) OwnClass.Serialize(ar); ar.Close(); File.Close(); } else { // File konnte nicht erstellt werden. } } // Du kannst natürlich auch ohne die Funktion Serialize von COwnClass auskommen. // Dafür musst du aber einen Operator << und >> bestimmen. inline CArchive& operator <<(CArchive& ar, COwnClass& oc) { ar << oc.GetInteger() << oc.GetName(); oc.GetTime().Serialize(ar); return ar; }; inline CArchive& operator >>(CArchive& ar, COwnClass& oc) { ar >> oc.GetInteger() >> oc.GetName(); oc.GetTime().Serialize(ar); return ar; }; // Und danach halt eben diese Operatoren benutzen wie auf int, floats usw.
Hoffe ich konnte da ein wenig Licht ins dunkle bringen und dich auf ein paar Spuren für weiter Hilfe bringen, z.b. die MSDN *g*
Grüssli
-
Hallo
Erstmal danke. Ich habe nun eine Klasse, die folgenden Mimber hat:
CArray<MemberVariable> MemVars;
Wenn ich nun folgende Funktion schreibe:
void ClassData::Serialize(CArchive& ar) { if (ar.IsStoring()) { // storing code ar<<MemVars; } else { // loading code ar>>MemVars; } }
bekomme ich immer folgende Fehlermeldung:
error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'CArchive' (or there is no acceptable conversion)
Ich dachte, es sei gerade der Vorteil, dass alles in das Archiv schreiben kann, was ich will. In diesem Fall scheint das aber nicht zu gehen.
chrische
-
Ehm lies doch die Fehlermeldung. CArray weisst kein << operator in der Klasse auf für CArchive. Dafür hat es ein CArray::Serialize, wo ich allerdings oft ein wenig probs hatte. Ich selber schreibe meistens lieber selber jedes einzelne Element des Arrays in das Archiv.
CArchive ist sehr nützlich, wenn man es richtig anwendet
Man kann übrigens grundsätzlich alle Grundtypen von C++, bzw. C über die Operatoren reinschreiben. CString ist da so eine Ausnahme. MFC Klassen lassen sich meistens über eine Funktion Serialize in ein CArchive-Objekt abspeichern.Grüssli
-
CArray::Serialize verwendet die Template-Funktion SerializeElements. Die ist für einige Typen (CString, CComBSTR, ...) spezialisiert, alles andere wird einfach mittels Write() rausgeschrieben, funktioniert also nur mit PODs. Wer also nicht-PODs mit CArray::Serialize (oder CList::Serialize, ...) verwenden möchte ist gut beraten vorher SerializeElements zu spezialisieren.
Siehe auch "MFC Library Reference, SerializeElements" und "MFC Library Reference, How to: Make a Type-Safe Collection".
-
Hallo
Ich habe es nun geschafft alle Daten in eine Datei zu schreiben, wenn ich diese Datei aber auswähle und dann die Daten von Ihr laden will, bekomme ich immer folgende Fehlermeldung:
Access to "Dateiname" denied
Das ganze erscheint in einer Messagebox.
Hier mal etwas Code:
void LoadAndSave::OnBnClickedLoadclass() { CFileDialog Dialog(TRUE, _T(".cfg"), _T(""), OFN_EXPLORER, _T("ClassGenerator-Datei (*.cfg) |*.cfg|")); if(Dialog.DoModal() == IDOK) { CFile File; File.Open(Dialog.GetPathName(), CFile::modeWrite); CArchive Archive(&File, CArchive::load); pMainWnd->Data.Serialize(Archive); MessageBox(_T("Die Klasse wurde geladen.")); } }
Data.Serialize sieht so aus:
void ClassData::Serialize(CArchive& ar) { if (ar.IsStoring()) { // storing code MemVars.Serialize(ar); } else { // loading code MemVars.Serialize(ar); } }
Was kann ich denn gegen diese Fehlermeldung tun?
chrische
-
Dein Problem liegt bestimmt beim öffnen der Datei, wenn Du aus der Datei etwas laden willst, dann mußt du CFile::modeRead benutzen. Ach so, und wenn Du das file nicht mehr benötigts solltest du es auch mit Close() schließen.
Gruß Matthias
-
Hallo
Ich habe es selber noch herausgefunden. Es ist antürlich so, dass man, wenn man laden will die Datei zum Lesen und nicht zum Schreiben öffnen muss.
Ich dachte, dass wenn CFile den Scope verlässt, dass dann automatisch die Datei geschlossen wird.
chrische