speichern von Ojekten?

  • #1
L

luzidius

Guest
Hallo forum,

ich schreibe aus indien, wo ich eine software zur erstellung von Stundenplaenen fuer ein Slumschule programmiere.

Dazu moechte ich objekte von der klasse Teacher in einer verketteten liste verwalten und die einzelnen ojekte binaer abspeichern.

Beispiel fuer die klasse Teacher:

class Teacher
{
char* name
Teacher* next
}


Dann habe ich, um das schreiben und lesen auszuprobieren, zwei funktionen formuliert:

Schreiben()
{
char* pszFileName = c:\\test\\myfile.dat;
CFile myFile;
CFileException fileException;


if ( !myFile.Open( pszFileName, CFile::modeCreate |
CFile::modeReadWrite ), &fileException )
{
TRACE( Can't open file %s, error = %u\n,
pszFileName, fileException.m_cause );
}

Teacher* teach= new Teacher
teach->name=test;

myFile.Write( &teach, sizeof( class Teacher ) );

myFile.Close();


}


Naechste funktion zum lesen der daten:

Lesen()
{

char* pszFileName = c:\\test\\myfile.dat;
CFile myFile;
CFileException fileException;
UINT nActual = 0;
Teacher* temp;

if ( !myFile.Open( pszFileName, CFile::modeReadWrite ), &fileException )
{
TRACE( Can't open file %s, error = %u\n,
pszFileName, fileException.m_cause );
}

myFile.SeekToBeginn();

nActual = myFile.Read( &temp, sizeof( class Teacher ) );

AfxMessageBox(test);
myFile.Close();

}


Wenn ich das programm starte und einen Teacher einlese, und danach, aber waehrend der selben laufzeit die funktion Lesen() aufrufe, wird die AfxMessageBox(test) angezeigt.

Schliese ich die anwendung, starte sie neu und rufe die lesen funktion auf zeigt die AfxMessageBox nichts mehr an; Als waere der name nur noch .
Die myFile.dat, ist aber immernoch beschrieben.
Ich habe das selbe vorher mit fopen, fread, fwrite usw. versucht. Gab den selben fehler.

weis jemand woran das liegen koennnte, ich schmohre naehmlich aschon seit tagen auf diesem problem?
 
  • #2
Ich nehme mal an, du hast das jetzt aus dem Gedächtnis getippt? Denn so wird es sicher nicht kompiliert ...

Wenn ich dazu komme, schreibe später ich noch ein paar Sachen, die dir vielleicht weiterhelfen.

Aber auf die Schnelle:

Code:
class Teacher
{
public: // geht sonst so eh nicht
  char* name;
  Teacher* next;
};

...
myFile.Write( &teach, sizeof( class Teacher ) ); 
...
nActual = myFile.Read( &temp, sizeof( class Teacher ) ); 
...

In Teacher stehen 2 Zeiger. sizeof( class Teacher ) gibt die Größe der beiden Zeiger an.
Du willst aber sicher den Namen des Lehres in der Datei speichern und nicht 2 Zeiger ...
Das Speichern des next-Zeigers würde dir auch nichts bringen, denn die Liste ist ja eine dynamische Datenstruktur.  Du kannst die ganzen Einträge mit next später nicht einfach wieder laden und - schwupps - ist die Liste wieder hergestellt ... (Du hast ja noch keine Liste, aber das schon vorweg.)
myFile.Write( &teach, sizeof( class Teacher ))
teach ist auch schon ein Zeiger, &teach ist ein Zeiger auf einen Zeiger. Darum ist das:

aber waehrend der selben laufzeit die funktion Lesen() aufrufe, wird die AfxMessageBox(test) angezeigt.

auch nur ein dummer Zufall. Du schreibst die Adresse des Objektes in die Datei und liest sie dann gleich wieder; deswegen zeigt teacher in Lesen() auch wieder auf das gleiche Objekt im Speicher. Das Programm könnte sogar abstürzen, denn du versucht in Lesen(), 8 Byte aus der Datei [sizeof(class Teacher)] in 4 Byte [temp] zu schreiben (und in Schreiben() liest du aus dem Speicher 4 Byte hinter->teach').
 
  • #3
Ich habe versucht was aus deinen ratschlaegen zu machen, bin aber nicht weit gekommen.

koenntest du mir bitte ein beispiel dafuer geben, wie ich objekte von der Klasse Teacher binaer speichern und auslesen kann, damit ich sie nach dem einlesen in eine liste einfuege.

class Teacher
{
public:
char getname();
void setname(char nam[30]);
void setnext( Teacher* nex)
char name[30];
Teacher* next;
BOOL a, b, c;

};

danke
alex
 
  • #4
luzidius schrieb:
Ich habe versucht was aus deinen ratschlaegen zu machen, bin aber nicht weit gekommen.

Das waren ja noch keine Ratschläge, sondern bloß eine Beschreibung der Fehlerursache  ... :)

koenntest du mir bitte ein beispiel dafuer geben

Klar, mache ich nachher.

// Edit

Sry, bin nicht mehr dazu gekommen. Montag ...
 
  • #5
Gleich vorweg: Bitte nicht selbst mit Listen u.ä. rumfummeln, sondern MFC- oder STL-Collections verwenden. Ich weiss nicht, welcher Kompiler bzw. welche MFC- und STL-Version dir zur Verfügung steht und ob damit alles so funktioniert. Wenn nicht (oder wenn du weitere Fragen hast), melde dich einfach.

In einer echten Anwendung (u.a.):
- Intensivere Fehlerprüfung und Testen auf Plausibilität der geladenen Daten
- Statt C-Strings besser std::string oder CString der MFC verwenden
- Evtl. CArchive verwenden, wie in der MFC Document-View-Architektur vorgesehen
- Ein kleiner Header ist praktisch (Kennzeichen, z.B. TCH, Anzahl der Objekte und Versionsnummer)
- Gegebenenfalls andere MFC- bzw STL-Container verwenden, wenn sinnvoll
- Über XML nachdenken
- Je nach Umfang der Daten über ein (kostenloses) Datenbanksystem nachdenken

Code:
// Zusammengestoppelte Teacher-Klasse als Beispiel
class Teacher
{
private:
  enum {WhatEverLen= 3, MaxNameLen= 30}; // nötig für VC6 und kleiner
  char name[MaxNameLen];
  bool whatever[WhatEverLen]; // statt a, b, c
  int  shoesize; // als Beispiel ;)
  void arrinit();
public:
  Teacher();
  Teacher(const char* name);
  Teacher(const char* name, int shoesize);
  const char* getName() const;
  void setName(const char* const);
  int  getShoeSize();
  void setShoeSize(int);
  bool getWhatEver(int idx);
  void setWhatEver(int idx, bool value);
  bool operator== (const Teacher&);
  bool operator< (const Teacher&); // Zum Sortieren (optional)
  friend ostream& operator<< (ostream& os, const Teacher&);
  friend istream& operator>> (istream& is, Teacher& teacher);
};

Hinweis: Wenn du den Speicher für->name' mit new allokieren würdest
class Teacher
{
private:
  char* name;
...
name= new char[length];
...
oder generell etwas anderes als einfache Variablen und Arrays in der Klasse vorhanden wäre, würde sich einiges ändern. Du solltest dann u.a. den Default-Konstruktor ändern und einen Copy-Konstruktor sowie einen Zuweisungs-Operator schreiben. Sonst kannst du z.B. Teacher-Objekte nicht fehlerfrei kopieren. Auch das Schreiben und Lesen der Daten müsste teilweise etwas anders implementiert werden, da, wenn du in diesem Fall einfach das Objekt als ganzes speicherst, wieder nur Zeiger statt Daten gespeichert würden.

Code:
#include stdafx.h

#include <fstream>
#include <list>             // für std::list
#include <afxtempl.h> // für CList

using namespace std;

bool copyStr(char* target, const char* source, unsigned int targetLen);

Code:
Teacher::Teacher()
  : shoesize(0) {
  arrinit();
}

Teacher::Teacher(const char* _name, int _shoesize)
  : shoesize(_shoesize) {
  arrinit();
  copyStr(name, _name, MaxNameLen); // oder Exception, wenn false
}

Teacher::Teacher(const char* const _name)  : shoesize(0) {
  arrinit();
  copyStr(name, _name, MaxNameLen); // oder Exception, wenn false
}

void Teacher::arrinit() {
  name[0]=->\0';
  for(int widx= 0; widx < WhatEverLen; ++widx)
    whatever[widx]= false;
}

bool Teacher::operator== (const Teacher& rhs)
{
  // Beispiel: Identisch, wenn gleiche Namen und gleiche Schuhgröße -
  // du musst dann eigene Bedingungen festlegen.
  return (strcmp(name, rhs.name) || (shoesize != rhs.shoesize) ? false : true);
}

bool Teacher::operator< (const Teacher& rhs)
{
  // Vergleich nach Namen
  return (strcmp(name, rhs.name) < 0 ? true : false);
}

const char* Teacher::getName() const {
  return name;
}

void Teacher::setName(const char* _name) {
  if(!copyStr(name, _name, MaxNameLen))
    throw(Teacher::setName - string error); // Bsp., dann richtige Exceptions
}

int Teacher::getShoeSize() {
  return shoesize;
}

void Teacher::setShoeSize(int ssi) {
  shoesize= ssi;
}

bool Teacher::getWhatEver(int idx)
{
  if(idx > WhatEverLen-1)
    throw(Teacher::getWasauchimmer - index error);
  return whatever[idx];
}

void Teacher::setWhatEver(int idx, bool value) {
  if(idx > WhatEverLen-1)
    throw(Teacher::setWasauchimmer - index error);
  whatever[idx]= value;
}

ostream& operator<< (ostream& os, const Teacher& teacher) {
  os << teacher.name <<->\n'; // mit echter Länge statt dem komplettem Array
  os.write((char*)&teacher.whatever, sizeof teacher.whatever);
  os.write((char*)&teacher.shoesize, sizeof teacher.shoesize);
  return os;
}

istream& operator>> (istream& is, Teacher& teacher) {
  is.getline(teacher.name,->\n'); // natürlich hier dann auch korrekt lesen
  is.read((char*)&teacher.whatever, sizeof teacher.whatever);
  is.read((char*)&teacher.shoesize, sizeof teacher.shoesize);
  return is;
}

bool copyStr(char* target, const char* source, unsigned int targetLen)
{
  if((!source) || (strlen(source) > targetLen-1))
    return false;
  strcpy(target, source);
  return true;
}

Code:
void TestMFC()
{
  // Liste erstellen
  CList<Teacher, const Teacher&> tlist;
  tlist.AddTail(Teacher(Michael, 57));
  tlist.AddTail(Teacher(Frank,  38));
  tlist.AddTail(Teacher(Mary, 39));

  // Schreiben

  CFile outfile;
  CFileException ex;
  CString filename(e:\\temp\\test.dat);

  if (!outfile.Open(filename, CFile::modeWrite |
    CFile::shareExclusive | CFile::modeCreate, &ex))
  {
    TCHAR szError[1024];
    ex.GetErrorMessage(szError, 1024);
    AfxMessageBox(szError);
    return;
  }

  try {
    POSITION pos = tlist.GetHeadPosition();
    for (int i=0; i < tlist.GetCount(); ++i) {
      Teacher t= tlist.GetNext(pos);
      // Hier schreiben wir im Gegensatz zu operator<< feste Blockgrößen
      // (ginge mit operator<< natürlich auch)
      outfile.Write(&t, sizeof(t));
    }
  }
  catch(CFileException* pEx) {
    pEx->ReportError();
    outfile.Close();
    return;
  }
  outfile.Close();

  // Lesen

  CList<Teacher, const Teacher&> tlist2;
  CFile infile;

  if (!infile.Open(filename, CFile::modeRead
    | CFile::shareDenyNone, &ex)) {
    TCHAR szError[1024];
    ex.GetErrorMessage(szError, 1024);
    AfxMessageBox(szError);
    return;
  }

  try {
    UINT size= sizeof(Teacher);
    UINT read;
    for(;;) {
      Teacher t;
      read= infile.Read(&t, size);
      if(read == size)
        tlist2.AddTail(t);
      else
        break;
    }
    if(read > 0) { // Darf nicht sein beim letzten Lesevorgang
      AfxThrowFileException(CFileException::endOfFile, -1, filename);
    }
  }
  catch(CFileException* pEx) {
    pEx->ReportError();
    infile.Close();
    return;
  }
  infile.Close();

  // Teacher-Daten anzeigen (Namen)
  POSITION pos = tlist2.GetHeadPosition();
  CString str;
  for (int i=0; i < tlist2.GetCount(); ++i) {
    Teacher t= tlist2.GetNext(pos);
    str+= t.getName();
    str+= \n;
  }
  AfxMessageBox(str);

}

Code:
void TestSTL()
{
  // Liste erstellen
  list<Teacher> tlist;
  tlist.push_back(Teacher(Michael, 57));
  tlist.push_back(Teacher(Frank,  38));
  tlist.push_back(Teacher(Mary, 39));

  char filename[]= e:\\temp\\test.dat;

  // Liste in eine Datei schreiben
  try {

    tlist.sort(); // Nur als Beispiel, was man ohne großen Aufwand mit der STL machen
                  // kann; dazu ist operator< in Teacher nötig.

    ofstream outfile;
    outfile.open(filename, ios::out);
    if(!outfile.is_open())
      throw Kann Datei nicht zum Schreiben oeffnen;

    ostream_iterator<Teacher> output(outfile, );

    copy(tlist.begin(), tlist.end(), output);

    outfile.close();

    // Alternativ könnten wir auch über die Liste iterieren (ähnlich wie unten
    // bei Liste anzeigen) und einzeln die Teacher-Objekte
    // per << nach outfile schreiben:
    /*
    list<Teacher>::const_iterator pos;
      for (pos = tlist.begin(); pos != tlist.end(); ++pos)
        outfile << *pos;
    */
  }
  catch(char *str) {
    AfxMessageBox(str);
    return;
  }

  // Alternativ könnten wir auch über die Liste iterieren und einzeln
  // die Teacher-Objekte per >> von infile lesen.

  // Liste lesen

  list<Teacher> tlist2;
  // Wir könnten auch tlist leeren [tlist.clear()] und hier verwenden. Ohne clear()
  // würde die neuen Objekte zu den 3 alten hinzugefügt.

  try {

    ifstream infile;
    infile.open(filename, ios::in);
    if(!infile)
      throw Kann Datei nicht zum Lesen oeffnen;

    istream_iterator<Teacher> input(infile);
    istream_iterator<Teacher> eof;

    copy(input, eof, back_inserter(tlist2));

    // Alternativ könnten wir auch über die Liste iterieren und einzeln
    // die Teacher-Objekte per >> von infile lesen.
  }
  catch(char *str) {
    AfxMessageBox(str);
    return;
  }

  // Teacher-Daten anzeigen (Namen)
  list<Teacher>::const_iterator pos;
  char str[200];
  strcpy(str, Objekte:\n);
  for (pos = tlist2.begin(); pos != tlist2.end(); ++pos) {
    strcat(str, pos->getName());
    strcat(str, \n);
  }
  AfxMessageBox(str); 

}
 
Thema:

speichern von Ojekten?

ANGEBOTE & SPONSOREN

Statistik des Forums

Themen
113.840
Beiträge
707.963
Mitglieder
51.494
Neuestes Mitglied
Flensburg45
Oben