fread() klappt nicht :O



  • Hallo! 🙂
    ich habe eine Aufgabe bekommen in der ich ein Teilproblem nicht gelöst bekomme^^. Folgende typedef Vereinbarungen waren schon vorimplementiert:

    // Vereinbarung von Datentypnamen fuer die Nutzdaten in der Struktur
      // -----------------------------------------------------------------------
      typedef char              airport_t[41];  // Name des Flughafens
      typedef unsigned char     lisnr_t;        // Nr. des Listenelements
      typedef char              carrier_t[7];   // Flugnummer
      typedef unsigned char     dtime_t;        // Abflugzeit Stunde, Minute
      typedef char             *pdest_t;        // Zeiger auf Zielort
    
      // Vereinbarung der Struktur fuer die Listenelemente
      // -----------------------------------------------------------------------
      typedef struct depart_e {                 // Etikett Listenelement
                 struct depart_e  *pnext;       // Zeiger auf Nachfolger
                 struct depart_e  *pprev;       // Zeiger auf Vorgaenger
                 lisnr_t           LisNr;       // Nummer des Listenelements
                 carrier_t         Carrier;     // Flugnummer
                 dtime_t           TDh;         // Abflugzeit Stunde
                 dtime_t           TDm;         // Abflugzeit Minute
                 pdest_t           Pdest;       // Zeiger auf dynamischen
                                                // String mit Zielort
              }  depart_t;                      // Typname Listenelement
      typedef depart_t  *pdepart_t;             // Typname Zeiger auf Listenelement
      typedef pdepart_t *ppdepart_t;            // Typname Zeiger auf Zeiger LE
    
      // Typen-Identitaets-Vereinbarungen zur Nutzung der Bibliothek lislib
      // ***********************************************************************
      // Die Komponenten-Namen pnext und pprev sind bereits in der Struktur
      // fuer ein Listenelement depart_t = le_t vereinbart.
      typedef depart_t             le_t;        // Listenelement
      typedef pdepart_t            ple_t;       // Zeiger auf Listenelement
      typedef ppdepart_t           pple_t;      // Zeiger Zeiger auf LE
    

    Mein Problem:
    ich soll aus einer Binärdatei Datensätze lesen und diese dann in einer verketteten Liste abspeichern.
    Was ich dabei nicht hinbekomme ist das lesen mit fread aus der Binärdatei.
    Habe zum Test erstmal versucht den Flughafennamen ( das klappt )und den ersten
    Datensatz zu lesen ( und das klappt nicht ).

    `Byte Inhalt der Binärdatei "abflug1.dat" (Hexdump)


    000000 46 61 6E 74 61 73 79 2D 43 69 74 79 00 4C 48 31

    000010 32 33 34 08 00 4C 6F 6E 64 6F 6E 00 42 41 31 32

    000020 33 34 08 0F 50 61 72 69 73 00 53 41 37 39 37 38

    000030 08 14 41 6D 73 74 65 72 64 61 6D 00 4C 58 34 37`
    .........usw

    Die Binärdatei enthält folgende Daten mit dem Namen des Flughafens und der Abflüge:

    Fantasy-City | Flughafenname

    LH1234 08 00 London | Flugnummer, Stunde, Minute, Flugziel
    BA1234 08 15 Paris
    SA7978 08 20 Amsterdam

    ....usw

    // ****************************************************************************
    // Aufbau der doppelt verketteten Liste aus der Binaerdatei
    // ****************************************************************************
    void readDataFromFile( char* filename, ppdepart_t pproot, airport_t AirPort ) {
    
      FILE *fp;             /* Dateizeiger festlegen                             */
    
      typedef struct {      /* Hilfsstruktur zum Daten einlesen                  */
        carrier_t flugNr;   /* Flugnummer                                        */
        dtime_t   h;        /* Abflugzeit Stunde                                 */
    	dtime_t   m;        /* Abflugzeit Minute                                 */
        pdest_t   ziel;     /* Zeiger auf dynamischen String mit Zielort         */
      }flug_t;              /* Typname Listenelement                             */
    
      flug_t flug;
    
      fp = fopen( filename, "rb" );                      /* binäre Datei oeffnen */
      fread( AirPort, sizeof(airport_t), 1, fp );        /* Flughafenname lesen  */
      fread( &flug, sizeof(flug_t), 1, fp );             /* 1 Datensatz lesen    */
    
      fclose( fp );                                      /* Datei schliessen     */
      printf("\n     Flugnummer ist: %s",flug.flugNr);   /* funzt nicht!         */
      printf("\n   Uhrzeit: %2.2d:%2.2d",flug.h, flug.m);/* funzt nicht!         */
    
    } // readDataFromFile()   ist natuerlich noch nicht fertig^^
    

    Weiß jemand wieso Zeile 19. nicht funktioniert? 😕 😮

    Gruß
    Johann



    1. Du überprüfst nicht den Rückgabewert von fopen. Was wenn die Datei nicht geöffnet wurden konnte? Dann greifst du auf einen NULL-Zeiger zu.
    2. Du darfst nicht direkt in eine Struktur reinlesen. Zumindest nicht, wenn du portable Dateien haben willst. Richtig wäre Element für Element einzulesen / zu schreiben.


    1. Als ersten Argument erwartet fread einen Zeiger auf deinen Buffer, worein fread lesen soll, also Typ void*. Du übergibst eine Struktur, airport_t. Richtig wäre, wenn du einen Zeiger auf deine Struktur übergibst, also airport_t*. Benutze hierzu den Adress-Operator &.


  • airport_t ist keine struktur, der parameter ist ok.



  • Mein Fehler, verzeihung. Ich hab nicht genau nachgeguckt und nahm an, dass airport_t eine Struktur ist. 😉



    1. Du überprüfst nicht den Rückgabewert von fopen. Was wenn die Datei nicht geöffnet wurden konnte?

    ich weiß, das ändere ich auch nachher, aber vorerst ist die Datei vorhanden. Nur schaffe ich es nicht daraus zu lesen.

    typedef struct {      /* Hilfsstruktur zum Daten einlesen                  */
        carrier_t flugNr;   /* Flugnummer                                        */
        dtime_t   h;        /* Abflugzeit Stunde                                 */
    	dtime_t   m;        /* Abflugzeit Minute                                 */
        pdest_t   ziel;     /* Zeiger auf dynamischen String mit Zielort         */
      }flug_t;              /* Typname Listenelement                             */
    

    ist nur eine Hilfsstruktur damit ich nur einmal fread benutze. Nachher wird das eingelesene in eine neue dynamische Struktur kopiert.

    Ich habe natürlich auch anstatt das:

    fread( &flug, sizeof(flug_t), 1, fp );
    

    dieses hier versucht:

    //typedef struct {
        carrier_t flugNr;
        dtime_t   h;
    	dtime_t   m;
        pdest_t   ziel;
      //}flug_t;
    
      //flug_t flug;
    
    fread( &flugNr, sizeof(carrier_t), 1, fp );
    fread( &h, sizeof(dtime_t), 1, fp );
    fread( &m, sizeof(dtime_t), 1, fp );
    fread( &ziel, sizeof(pdest_t), 1, fp );
    

    Nur das funktioniert genauso wenig.

    Gruß
    Johann



  • Kann das sein, das du eine Textdatei mit fread einliest?



  • Also die Datei hat die Endung .dat und ist laut Aufgabenstellung binär. Nur seltsam ist wenn ich die Endung in .txt ändere dann kann ichs lesen?!

    Fantasy-City LH1234 London BA1234Paris SA7978Amsterdam LX4711-Rom IA9846	Madrid via Paris LH2375	Moskau BA5982	(Kopenhagen LH7407
     Mnchen LH4328
    Wien BA2376
    -New York LH4198 Los Angeles via Chicago SA2143Tokio IA6937-Sidney LX8809 Las Palmas LH9182Buenos Aires LH5674
     Bogota
    

    Also ich schaffe es den Flughafennamen "Fantasy-City" mit

    fread( AirPort, sizeof(airport_t), 1, fp );
    

    zu lesen, allerdings aber auch mit:

    fgets( AirPort, sizeof(airport_t), fp );
    

    hmm rätselhaft 😃 😮



  • Johann2 schrieb:

    `Byte Inhalt der Binärdatei "abflug1.dat" (Hexdump)


    000000 46 61 6E 74 61 73 79 2D 43 69 74 79 00 4C 48 31

    000010 32 33 34 08 00 4C 6F 6E 64 6F 6E 00 42 41 31 32

    000020 33 34 08 0F 50 61 72 69 73 00 53 41 37 39 37 38

    000030 08 14 41 6D 73 74 65 72 64 61 6D 00 4C 58 34 37`
    .........usw

    Die Zeichenketten sind 0 Terminiert.
    Mit fread( AirPort, sizeof(airport_t), 1, fp ); liest du 41 Zeichen ein,
    strlen("Fantasy-City") ist aber < 41.
    D.h. du überspringst einen Teil der Daten (Flugnummer, Stunde, Minute, Flugziel), dieser Teil landet in AirPort.



  • ahh stimmt, du hast recht^^

    habe das jetzt so gelöst:

    fread( AirPort, sizeof(airport_t), 1, fp ); /* Flughafennamen lesen */
    fseek(fp, strlen(AirPort)+1, SEEK_SET);     /* Dateilesezeiger zurueck */
    

    geht das auch noch anders?

    Gruß
    Johann



  • Johann2 schrieb:

    geht das auch noch anders?

    Joar, es geht auch ohne Filepointerhopping:

    #define AIRPORT_SLEN 63
    #define AIRPORT_BUFSIZE (AIRPORT_SLEN + 1)
    
    typedef char airport_t[AIRPORT_BUFSIZE];
    
    int main() {
    ...
    int c = 0;
    unsigned i = 0;
    airport_t airport = {0};
    FILE* fp = fopen (...
    ...
    while ( (c = fgetc(fp)) != EOF && c != 0 && i < AIRPORT_SLEN ) 
    	airport[i] = c, i++;
    
    if ( i == AIRPORT_SLEN ) 
    	printf ( "Airport name cropped! The cropped name is %s\n", airport );
    ...
    


  • Das Fehlersuchen ist bei mir immer der längste Part beim proggen, is das bei euch auch so? xD
    Da ist ein Wurm drin der sich ganzschön Hartnäckig hält! Vielleicht sieht ihn jemand?

    // ****************************************************************************
    // Aufbau der doppelt verketteten Liste aus der Binaerdatei
    // ****************************************************************************
    void readDataFromFile( char* filename, ppdepart_t pproot, airport_t AirPort ) {
    
      FILE *fp;             /* Dateizeiger                                       */
      dtime_t h, m;         /* Lesepuffer fuer TDh und TDm                       */
      ple_t proot = NULL,   /* interner Wurzelzeiger fuer neue Liste             */
    	    pakt;           /* Hilfszeiger fuer Listenaufbau                     */
      char buf[81],         /* Lesepuffer fuer Pdest und AirPort                 */
           inStr[81];       /* Programmhalt ( Funktion messeage() )              */
    
       int test = 0;//nur fuer test
    
      if( (fp = fopen(filename, "rb")) == NULL )  /* Datei nicht vorhanden??     */
          {
             message(NULL, "Fehler: Datei nicht gefunden [ret]: ", inStr);
          }
        else                                      /* Datei vorhanden..           */
          {
            fread( AirPort, sizeof(airport_t), 1, fp );  /* Flughafennamen lesen */
            fseek(fp, strlen(AirPort)+1, SEEK_SET);      /* Lesezeiger zurueck   */
    
            while(  /*!( fread( &buf, sizeof(carrier_t)-1, 1, fp ) )*/test < 3 ) {
    		  test++;      
              pakt = (ple_t)malloc(sizeof(le_t));        /* neues LE erstellen   */
    
              fread( &buf, sizeof(carrier_t)-1, 1, fp ); /* Flugnummer lesen     */
              fread( &h, sizeof(dtime_t), 1, fp );       /* Abflugstunde lesen   */
    		  fread( &m, sizeof(dtime_t), 1, fp );       /* Abflugminute lesen   */
              pakt->TDh = h; pakt->TDm = m;              /* Uhrzeit in LE        */
    		  strcpy( pakt->Carrier,buf);                /* FlugNr in LE         */
    
    		  /* Flugziel lesen und speichern / dann Lesezeiger zurueck          */
              fread( &buf, sizeof(buf), 1, fp );
              pakt->Pdest = (pdest_t) calloc( strlen(buf)+1, sizeof(char) );
              strcpy(pakt->Pdest, buf); 
    	      fseek(fp, -(sizeof(buf) - strlen(buf)-1), SEEK_CUR);
    
              llb_LE_Einketten( &proot, NULL, NULL, pakt ); /* Liste aufbauen    */
    		} /*  while( !( fread...                                             */
            fclose( fp );                                   /* Datei schliessen  */
            insertLisNr(proot);                             /* Liste nummerieren */
          } /* if( (fp = fopen(.. */
    
      *pproot = proot;                                  /* neue Liste uebergeben */  
    } // readDataFromFile()
    

    Es werden nur die beiden ersten Datensätze korrekt angezeigt. Der dritte ist Schrott und wenn ich die Schleife in der 24. Zeile 4 Durchläufe machen lasse, gibts beim Ausführen nen Fehler.
    Interessant ist auch das ich den ersten und zweiten Datensatz problemlos mit free() wieder löschen kann, aber beim dem besagten dritten Datensatz gibts dann nen HeapError(CRT detected that the application wrote to memory after end of Heap buffer)

    Ich habe auch das einlesen pro Zeichen mit fgetc() versucht:

    for( int i = 0; i < 13; i++ ) AirPort[i] = fgetc(fp);// 13 Zeichen f. Fantasy-City\0
    
    		  for( int test = 0; test < 7; test++ )                // 7 Testdurchlaeufe
    		    {
                  pakt = (ple_t)malloc(sizeof(le_t));              // neues LE erstellen
    			  for( i = 0; i < (sizeof(carrier_t)-1); i++ )     // FlugNr sind 6 Zeichen,deswegen sizeof(carrier_t)-1
    			    {
    				  pakt->Carrier[i] = fgetc(fp);                
    			    }
                  pakt->Carrier[sizeof(carrier_t)] = '\0';         // FlugNr mit '\0' abschliessen
                  pakt->TDh = fgetc(fp);                           // Abflugstunde lesen, 1 unsigned char
    		      pakt->TDm = fgetc(fp);                           // Abflugminute lesen, 1 unsigned char
                  for( i = 0; '\0' != ( c = fgetc(fp) ); i++ )     // FlugZiel einlesen
    			    {
                      buf[i] = c;                                  // vorerst in buf speichern
    			    }
                  buf[i] = '\0';                                   // FlugZiel mit '\0' abschliessen
                  pakt->Pdest = (pdest_t)calloc( strlen(buf)+1, sizeof(char) );
                  strcpy(pakt->Pdest, buf);                        //das Eingelesene in LE speichern
    
                  llb_LE_Einketten( &proot, NULL, NULL, pakt );    // Liste aufbauen, LE's anhaengen
    		    } //for( int test..
    

    Wenn ich diesen Codeabschnitt zum Datei auslesen benutze, wird nur der erste Datensatz richtig
    angezeigt, also LH1234 08:00 London .
    die nächsten Datensätze werden auch alle richtig gelesen, nur wird zwischen den einzelnen
    Strukturelementen also FlugNr, Zeit, Flugziel noch kryptische Zeichen mit ausgegeben.
    Hier lassen sich aber auch alle Datensätze problemlos wieder mit aus der verketteten Liste
    ausketten und löschen mit free() .

    Hoffentlich finden den Fehler jemand :p

    Gruß
    Johann



  • Ich versteh schon wen mir keiner antworten will^^kostet immerhin Zeit.
    Ich habe den den Fehler gefunden, nur kapier ich den nicht. 😮

    Zeile 28. also

    fread( &buf, sizeof(carrier_t)-1, 1, fp ); /* Flugnummer lesen     */
    

    liest teilweise bis zu 19 Zeichen ein, wie ich mit strlen() festgestellr habe,
    obwohl sizeof(carrier_t)-1 6 char Zeichen sind.



  • Johann2 schrieb:

    Zeile 28. also

    fread( &buf, sizeof(carrier_t)-1, 1, fp ); /* Flugnummer lesen     */
    

    liest teilweise bis zu 19 Zeichen ein, wie ich mit strlen() festgestellr habe,
    obwohl sizeof(carrier_t)-1 6 char Zeichen sind.

    fread() macht keine Null am Ende, deshalb kannst du mit strlen() gar nicht feststellen, wie viel reingekommen ist. Zum Glück gibt die Rückgabe von fread() darüber Auskunft. Wenn du die mit dem 2. Argument multiplizierst, kommst du auf die Anzahl der Bytes.
    🙂



  • mir is bekannt das fread() kein 0 am Ende macht, find ich ja so seltsam.

    Wenn du die mit dem 2. Argument multiplizierst, kommst du auf die Anzahl der Bytes

    meinst du so:

    int test2 = fread( &(pakt->Carrier), sizeof(carrier_t)-1, 1, fp ); /* FlugNr   */
              printf("\neingelesene Bytes %d",(test2*(sizeof(carrier_t)-1)));
    

    test2 ist mal 8 mal 19...

    und carrier_t ist mit

    typedef char              carrier_t[7];   // Flugnummer
    

    definiert.



  • Johann2 schrieb:

    test2 ist mal 8 mal 19...

    Das kann nicht sein. Der Rückgabewert von fread() muss kleiner oder gleich dem dritten Parameter sein.
    🙂



  • habe den fehler gestern rausgefunden, es lag daran das diese Zeile im Code gefehlt hat:

    pakt->Carrier[sizeof(carrier_t)-1] = '\0';         // FlugNr mit '\0' abschliessen
    

    weil die FlugNr durch printf mit %s ausgegebenn wird.


Anmelden zum Antworten