Frage zu fread/fwrite (Anfänger)

Schnelle objektorientierte, kompilierende Programmiersprache.
rappel
Beiträge: 4
Registriert: Di Dez 01, 2009 2:13 pm

Frage zu fread/fwrite (Anfänger)

Beitrag von rappel » Di Dez 01, 2009 2:24 pm

Hallöle,

ich verzweifel gerade ein bischen an folgendem Code:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Kundendaten
{
  int   Kundennummer;
  char  Name[150];
  char  Adresse[200];
  char  Telefonnummer[15];
};


void loescheTastaturpuffer()
{
  int c;
  while( ((c = getchar()) != EOF) && (c != '\n') )
     ;
}


void setzeDatensatz (struct Kundendaten *k, int eingabe, int *datenGesetzt)
{
   int sizeName = 0, sizeAdr = 0, sizeTel = 0;   /* Groesse der Felder */

   sizeName = sizeof (k->Name);
   sizeAdr = sizeof (k->Adresse);
   sizeTel = sizeof (k->Telefonnummer);

   printf ("Setze Eintrag %d:\n\n", eingabe);

   printf ("Kundennummer: ");
   scanf ("%d", &k->Kundennummer);
   loescheTastaturpuffer();   /* sonst bleibt \n im Buffer */

   printf ("Name: ");
   fgets (k->Name, sizeName-1, stdin);

   printf ("Adresse: ");
   fgets (k->Adresse, sizeAdr-1, stdin);

   printf ("Telefonnummer: ");
   fgets (k->Telefonnummer, sizeTel-1, stdin);

      /* Null-Terminierung und \n entfernen */
//   k->Name[strlen(k->Name)-1] = '\0';
//   k->Adresse[strlen(k->Adresse)-1] = '\0';
//   k->Telefonnummer[strlen(k->Telefonnummer)-1] = '\0';

   *datenGesetzt = 1;   /* daran wird erkannt, dass schon Daten eingegeben wurden */
}


void zeigeDatensatz (struct Kundendaten *k, int i)
{
         printf ("Kundennummer: %d\n", k[i].Kundennummer);
         printf ("Name: %s\n", k[i].Name);
         printf ("Adresse: %s\n", k[i].Adresse);
         printf ("Telefonnummer: %s\n\n", k[i].Telefonnummer);
}

void DatenSpeichern (FILE *dateizeiger, struct Kundendaten *k, int *datenGesetzt)
{

   int i = 0;

   printf ("\nDaten werden gespeichert.\n\n");

   fseek (dateizeiger, 0, SEEK_SET);

   while ( i < 10 && datenGesetzt[i] == 1 && fwrite ( &k[i], sizeof(struct Kundendaten), 1, dateizeiger) ==1 )
    {
        printf ("Datensatz %d geschrieben...\n", i);
        zeigeDatensatz (k, i);
        i++;   /* in die Datei schreiben */
    }

   printf ("%d Elemente erfolgreich geschrieben!\n", i);

}

void Datenlesen (FILE *dateizeiger, struct Kundendaten *k, int *datenGesetzt)
{

    int i = 0;

    printf ("\nDaten werden gelesen.\n\n");

    fseek (dateizeiger, 0, SEEK_SET);

    while ( fread ( &k[i], sizeof(struct Kundendaten), 1, dateizeiger) ==1 )
    {
          printf ("Datensatz %d gelesen...\n", i);
          // zeigeDatensatz (k, i);
          if (&k[i].Kundennummer != 0)
           datenGesetzt[i] = 1;
          i++;

    } /* aus der Datei lesen */

}

void zeigeDatengesetzt (struct Kundendaten *k, int *datenGesetzt)
{

    int j;

    for (j = 0 ; j < 10; j++)
    {
        printf ("datenGesetzt [%d]: %d \n", j, datenGesetzt[j]);
        //zeigeDatensatz (k, j);
    }

}

void initall (struct Kundendaten *k)
{
    int i;

    for (i=0; i<10; i++)
    {
        k[i].Kundennummer = 0;
        k[i].Name[0] = '\0';
        k[i].Adresse[0] = '\0';
        k[i].Telefonnummer[0] = '\0';
    }
}

int main()
{
   struct Kundendaten kunden[10];
   unsigned int eingabe = 0;
   int          i = 0, j,
                datenGesetzt[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   FILE *dateizeiger;
   const char DATEINAME[] = "testoutput.dat";

   initall (kunden);

     /* Versuchen, die Datei mit r+ zum Lesen und Schreiben zu oeffnen */
   dateizeiger = fopen (DATEINAME, "r+");

   if (dateizeiger == NULL)   /* dann zum Schreiben oeffnen (neu anlegen) */
   {
      dateizeiger = fopen (DATEINAME, "w");

      if (dateizeiger == NULL)
      {
         fputs ("Fehler: Datei konnte nicht geoeffnet werden!\n", stderr);
         return EXIT_FAILURE;
      }
   }

   do
   {
      printf ("Welchen Eintrag verwenden: (1-10; 0 = Programmende; 11 = Speichern; 12 = Einlesen ; 13 = Zeige Datengesetzt) ");
      scanf ("%u", &eingabe);

      if (eingabe == 0)
      {
        fclose (dateizeiger);
         return 0;   /* Programmende */
      }
      else if (eingabe == 11)
      {
          DatenSpeichern (dateizeiger, kunden, datenGesetzt);
          continue;
      }
      else if (eingabe == 12)
      {
          Datenlesen (dateizeiger, kunden, datenGesetzt);
          continue;
      }
      else if (eingabe == 13)
      {
            zeigeDatengesetzt (kunden, datenGesetzt);
            continue;
      }
      else if (eingabe > 10)
         continue;   /* sonst wird der zulaessige Bereich ueberschritten */

      i = eingabe -1;   /* Array-Index geht von 0 bis 9, Benutzereingabe aber von 1 bis 10 */

         /* wenn noch keine Daten eingegeben wurden, einlesen */
      if (datenGesetzt[i] == 0)   /* false */
         setzeDatensatz (&kunden[i], eingabe, &datenGesetzt[i]);
      else   /* true */
      {   /* sonst Daten anzeigen */
            zeigeDatensatz (kunden, i);
      }
   }
   while (1);   /* Endlosschleife */

   fclose (dateizeiger);

   return 0;
}
So lange ich nicht versuche, die in der Datei gespeicherten Daten nach einem Programmende und neuem Aufruf wieder einzulesen (Funktion 12) funktioniert (zumindest scheinbar) alles. Ich habe jetzt schon mehrere Stunden Foren/Tutorials gelesen. Aber ich komme da echt nicht weiter.
Bin für jede Hilfe dankbar.

Rappel

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von Xin » Di Dez 01, 2009 2:43 pm

Okay, ich habe mir den Code jetzt mal in einen Editor kopiert und dann Deine Beschreibung gelesen.

Eine "Funktion12" gibt es nicht und wenn doch, so wäre das ein sehr unpassender Name.
Ich helfe Dir gerne, aber Du musst mir schon helfen, Dir zu helfen. Ich zähle nämlich jetzt nicht die Funktionen durch in der Hoffnung, dass Du und ich uns beide nicht verzählt haben.

Bitte beschreibe Deine Erwartung an das Programm und das, was Du beobachtest. Dann gucke ich es mir gerne mal an.

Ansonsten herzlich willkommen im Forum. ^^
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

rappel
Beiträge: 4
Registriert: Di Dez 01, 2009 2:13 pm

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von rappel » Di Dez 01, 2009 2:55 pm

Hallo Xin,
danke für das Willkommen und die schnelle Antwort.

Ja, ich über mich noch in der Ausdrucksweise :?

Also, die Funktion heist Datenlesen (und wird mit der Eingabe 12 aufgerufen....)

Das Programm soll eigentlich bis zu 10 Sätzen der Struktur Kundendaten einlesen (von der Tastatur, Funktion SetzeDatensatz), diese auf Wunsch speichern (in eine Datei, alle auf einmal, aber nur so viele, wie auch eingegeben wurden, Funktion Datenspeichern) und dann auch wieder einlesen (aus der Datei, dabei wieder rauskriegen, wie viele Datensätze eigentlich gesetzt sind, Funktion Datenlesen).
Natürlich werden die Daten auch angezeigt (manchmal auch zur Kontrolle, Funktionen zeigeDatensatz und zeigeDatengesetzt).
Die Funktion initall habe ich auch nur zur Sicherheit eingebaut.

Ich hoffe, jetzt wird es etwas klarer. ;)

rappel

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von Xin » Di Dez 01, 2009 3:05 pm

Ich gucke gleich mal, aber mein Essen ist jetzt fertig. ^^
Bin in einer Stunde wieder am Rechner.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
stampuhh
Beiträge: 211
Registriert: Sa Nov 07, 2009 4:39 pm
Wohnort: Paderborn

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von stampuhh » Di Dez 01, 2009 3:05 pm

Also bei mir läuft das Programm soweit ich das jetzt getestet habe.

Ich habe 3 Datensätze eingegeben. Programm geschlossen und wieder gestartet, 12 eingegeben und dann stand 3 mal Datensatz eingelesen (angefangen bei 0-2). Auf die konnte ich dann über die Nummern 1-3 zugreifen.

Oder hab ich da was falsches getestet?

gruß stampuhh
NachDenkSeiten.de

rappel
Beiträge: 4
Registriert: Di Dez 01, 2009 2:13 pm

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von rappel » Di Dez 01, 2009 3:11 pm

Also bei mir (Win XP) habe ich gerade noch mal die Daten-Datei gelöscht, 3 Datensätze eingegeben, gespeichert. Prog zu, neu auf, einlesen----> NIX
:shock:

Benutzeravatar
stampuhh
Beiträge: 211
Registriert: Sa Nov 07, 2009 4:39 pm
Wohnort: Paderborn

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von stampuhh » Di Dez 01, 2009 3:23 pm

Du hast recht. Unter XP klappt das ganze nicht. Mein Test war auf Linux ;)

Außerdem musste ich um das ganze kompilieren zu können die Reihenfolge der Bibliotheken noch ändern...

Er liest es irgendwie nicht ein :?:

Daraus schließe ich einfach mal da ist wieder irgendein Linux <-> Windows Problem schuld und überlasse das Feld dann Xin :P

edit: Vielleicht kann man das ganze auf die Speicher-Funktion beschränken. Ich habe grade mal die unter Linux geschriebene Datei eingelesen was ohne Probleme auch unter XP geklappt hat. => Speichern auf Windows funktioniert nicht richtig?

gruß stampuhh
NachDenkSeiten.de

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von nufan » Di Dez 01, 2009 3:58 pm

Ich habs zwar noch nicht ganz zum Laufen gebraucht, hier aber ein paar Anmerkungen:

* fread/fwrite wird für binäre Dateien verwendet. Du öffnest die Datei aber für Text.
* Wenn du nur mit "w" (bzw. "wb" was in deinem Fall korrekt wäre) kannst du nicht aus der Datei lesen, was du aber trotzdem im Menü als Option angibst.
* Du prüfst in den Schleifen zum Lesen und Schreiben ob du 1 Byte gelesen oder geschrieben hast. Die Funktionen geben aber die Anzahl der Bytes zurück (also sizeof (struct Kundendaten)).
* Du initialisierst den Array wie folgt:

Code: Alles auswählen

datenGesetzt[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Bei 0 musst du nur 1 mal anschreiben, also:

Code: Alles auswählen

datenGesetzt[10] = { 0 };
Deine Version ist nicht falsch, anders ists aber schöner.
* Du schreibst die Datensätze als komplette Struktur in die Datei. Doch was hast du in der Struktur? Einen Integer und 3 Zeiger. Der Integer ist ok, aber mit den Adressen der char-Arrays kannst du später nichts mehr anfangen, weil der Speicher früher oder später überschrieben wird, wenn das Programm beendet wird.
* Ich persönlich habe schlechte Erfahrungen mit Lesen + Schreiben gemacht, gerade in Kombination mit fseek. Wenn du die Datei schließt und neu aufmachst bzw. freopen verwendest bist du auf der sicheren Seite.

So, das wars vorerst von meiner Seite, hoffe es war was hilfreiches dabei :)

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von Xin » Di Dez 01, 2009 5:02 pm

Programm getestet unter Mac (gcc), läuft.
nufan hat geschrieben:Ich habs zwar noch nicht ganz zum Laufen gebraucht, hier aber ein paar Anmerkungen:

* fread/fwrite wird für binäre Dateien verwendet. Du öffnest die Datei aber für Text.
* Wenn du nur mit "w" (bzw. "wb" was in deinem Fall korrekt wäre) kannst du nicht aus der Datei lesen, was du aber trotzdem im Menü als Option angibst.
Das sieht mir auch nach dem Fehler hier aus: Windows verlangt das 'b' - bei Unix (Linux, Mac) spielt es keine Rolle.
nufan hat geschrieben:* Du prüfst in den Schleifen zum Lesen und Schreiben ob du 1 Byte gelesen oder geschrieben hast. Die Funktionen geben aber die Anzahl der Bytes zurück (also sizeof (struct Kundendaten)).
fread gibt die Anzahl der Datensätze zurück. Wenn ein Datensatz sizeof( struct Kundendaten ) groß ist, dann wird 1 * sizeof() Bytes gelesen - oder eben 0 mal...
nufan hat geschrieben:* Du initialisierst den Array wie folgt:

Code: Alles auswählen

datenGesetzt[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Bei 0 musst du nur 1 mal anschreiben, also:

Code: Alles auswählen

datenGesetzt[10] = { 0 };
Deine Version ist nicht falsch, anders ists aber schöner.
Subjektiv... ich empfinde die vorgeschlagene Version als semantisch falsch / mehrdeutig.
nufan hat geschrieben:* Du schreibst die Datensätze als komplette Struktur in die Datei. Doch was hast du in der Struktur? Einen Integer und 3 Zeiger. Der Integer ist ok, aber mit den Adressen der char-Arrays kannst du später nichts mehr anfangen, weil der Speicher früher oder später überschrieben wird, wenn das Programm beendet wird.
Irrtum: ein char [] ist zwar gleichwertig mit einem char *, aber sie sind nicht identisch! Das Array ist in die Struktur eingebettet und wird entsprechend auch geschrieben.
nufan hat geschrieben:* Ich persönlich habe schlechte Erfahrungen mit Lesen + Schreiben gemacht, gerade in Kombination mit fseek. Wenn du die Datei schließt und neu aufmachst bzw. freopen verwendest bist du auf der sicheren Seite.
Ich sehe hier diesbezüglich kein Problem. Unter Unix läuft es ja auch.
Der Ordnung halber würde ich die Dateioperationen allerdings auch zusammenfassen, so dass die Datei geschlossen ist, wenn sie nicht benötigt wird.

Die Lösung vermute ich ebenfalls sehr stark bei fopen( ..., "w+b" ).
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Frage zu fread/fwrite (Anfänger)

Beitrag von nufan » Di Dez 01, 2009 5:11 pm

Xin hat geschrieben:fread gibt die Anzahl der Datensätze zurück. Wenn ein Datensatz sizeof( struct Kundendaten ) groß ist, dann wird 1 * sizeof() Bytes gelesen - oder eben 0 mal...
Sorry, mein Fehler, hab mich in der Referenz verlesen...
Xin hat geschrieben:
nufan hat geschrieben: * Ich persönlich habe schlechte Erfahrungen mit Lesen + Schreiben gemacht, gerade in Kombination mit fseek. Wenn du die Datei schließt und neu aufmachst bzw. freopen verwendest bist du auf der sicheren Seite.
Ich sehe hier diesbezüglich kein Problem. Unter Unix läuft es ja auch.
https://www.proggen.org/forum/viewtopic ... 7387#p7387
Lief ziemlich ähnlich ab: zuerst prüfen ob Datei existiert, wenn ja zum Lesen und Schreiben öffnen, ansonsten neu anlegen. Bei mir hat damals rewind den Fehler verursacht, was mit fseek (file, 0, SEEK_SET); gleichzusetzen ist.

Antworten