Forum: PC-Programmierung beliebig viele Einträge aus Binärdatei auslesen und in Liste speichern


von Hans M. (fuxdancer)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich versuche gerade in C unter Verwendung des Befehls fread aus einem 
Binärfile Einträge einer bestimmen Form (siehe Anhang) herauszulesen.

Sie bestehen aus einem Header mit fixer Länge und aus einem Content mit 
zwei Strings variabler Länge.

Ich habs nun schon geschafft, dass ich den ersten Eintrag gesamt auslese 
und dann in eine einfach verkettete Liste speicher mit genau diesen 
Einträgen (ID, Latitude, ...).

Nun weiß ich aber nicht, wie viele solcher Einträge in der Datei 
gespeichert sind, hat jemand vielleicht eine Idee, wie ich genau so oft 
einlesen kann, bis kein Eintrag mehr vorhanden ist?

Meine bisherigen Gedankengänge:
Auf den return-Wert von fread schauen
http://www.cplusplus.com/reference/clibrary/cstdio/fread/
dieser liefert jedoch den gleichen Wert, wenn entweder ein Fehler 
passiert bzw. EOF erreicht wurde.
Also so wirds wohl nicht gehen

Könnte mir jemand einen Tipp gebn?

von Peter II (Gast)


Lesenswert?

Hans M. schrieb:
> Auf den return-Wert von fread schauen

das ist schon richtig. Du kannst ja nach einem Fehler mit feof prüfen ob 
du am ende bist.

Man könnte auch vorher die Dateigröße ermitteln und rechenn wie viele 
einträge denn drin sind.

von Hans M. (fuxdancer)


Angehängte Dateien:

Lesenswert?

Das mit feof hab ich gerade gesehen, nur weiß ich nicht, wie man das in 
mein Programm implementiert.
Ich hab im Anhang mal so Binärfile, aus welchem ich einlese, angehängt, 
funktioniert dort dieses feof?

Könntest du mir zu diesem feof vielleicht ein Beispielprogramm zeigen?

von Hans M. (fuxdancer)


Lesenswert?

Funktioniert das genauso wie in diesem Example hier?
1
 #include <stdio.h>
2
3
int main(void)
4
{
5
    char buffer[256];
6
    FILE * myfile;
7
  
8
    myfile = fopen("some.txt","r");
9
  
10
    while (!feof(myfile))
11
    {
12
        fgets(buffer,256,myfile);
13
        printf("%s",buffer);
14
    }
15
  
16
    fclose(myfile);
17
    
18
    return 0;
19
}

von Hans M. (fuxdancer)


Lesenswert?

Ich habs glaube ich geschafft, habs mit dieser While-Schleife mit dem 
feof versucht und ich kann tatsächlich alle Einträge auslesen :).

von Karl H. (kbuchegg)


Lesenswert?

Hans M. schrieb:
> Funktioniert das genauso wie in diesem Example hier?
>
> #include <stdio.h>
>
> int main(void)
> {
>     char buffer[256];
>     FILE * myfile;
>
>     myfile = fopen("some.txt","r");
>
>     while (!feof(myfile))

schon falsch.

C sieht nicht in die Zukunft! In C muss ein Leseversuch erfolgt sein und 
aufgrund von eof fehlgeschlagen sein, ehe eof gemeldet wird.

Alle C-Lesefunktionen sind so gestrickt, dass man ihren Return-Wert 
auswertet um damit die Schleife zu steuern, die das Lesen regelt. Erst 
danach, nach der Schleife, überprüft man mittels feof ob die Schleife 
abgebrochen wurde weil ein Fehler auftrat oder weil das Dateiende 
erreicht wurde.

Die typische Forenfrage zu deinem Code lautet:
Hilfe, warum wird mein letzter Datensatz zweimal eingelesen.


Und so gehts richtig:
1
   ....
2
3
   while( fgets(buffer,256,myfile) ) {    // oder fread oder ...
4
     printf("%s",buffer);
5
   }
6
7
   if( !feof(myfile) ) {
8
     printf( "Fehler beim Lesen der Datei!" );
9
     ....
10
   }
11
12
   fclose(myfile);

von Hans M. (fuxdancer)


Lesenswert?

Ich schaffs leider doch nicht -_-
ich komme leider aus dieser Schleife nicht mehr heraus.

Ausschnitt aus der Hauptfunktion
1
  while(!(no_more_entries_flag))
2
  {  
3
    first_element_of_list = readHeader(input_file, 
4
      error_code, &no_more_entries_flag);
5
6
    if(*error_code != 0)
7
    {
8
      closeFile(&input_file);
9
      return first_element_of_list;
10
    } 
11
  
12
    
13
    first_element_of_list = readContent(input_file,
14
      first_element_of_list, error_code);
15
    
16
    if(*error_code != 0)
17
    {
18
      closeOpenedFile(&input_file);
19
      return first_element_of_list;
20
    }
21
    
22
    first_element_of_list =
23
      first_element_of_list->next;
24
    
25
    number_of_entries++;
26
  }

Ausschnitt aus der Header-Einlesefunktion, wie ich überprüfe, ob ich das 
Ende des Files erreicht habe
1
  if((fread(&(first_element_of_list->header), sizeof(Header),
2
    1, input_file)) != 1)
3
  {
4
    first_element_of_list = NULL;
5
    return first_element_of_list;
6
  }
7
    
8
  if(feof(input_file) != 0) // hier will ich eigentlich herauskommen
9
  {
10
    *no_more_entries_flag = 0; 
11
    return first_element_of_list;  
12
  }

Das Problem ist, das ich, wenn zB. 3 Einträge einzulesen habe, dann lies 
ich die 3 Einträge ein, komme in den 4. Schleifendurchlauf der While und 
dort kommt es dann zu einem Segmentation, d.h. ich komme gar nicht mehr 
in die Funktion readHeader (ein printf wird hier nicht mehr angezeigt, 
jedoch wenn ich es am Anfang der While einfüge, schon)

Hat jemand eine Idee, wo mein Fehler liegen könnte, ich kann leider den 
ganzen Code nicht posten, weil das bei der Abgabe mit Plagiatgefahr 
enden könnte!

Ich könnte auch jemanden den Code senden, aber der darf ihn dann halt 
nicht veröffentlichen.

von Rolf M. (rmagnus)


Lesenswert?

Hans M. schrieb:
> if(feof(input_file) != 0) // hier will ich eigentlich herauskommen
>   {
>     *no_more_entries_flag = 0;
>     return first_element_of_list;
>   }

Diese ganzen Negierungen machen es nicht leichter zu lesen:

wenn feof nicht null ist, lösche das Flag, das sagt, ob keine weiteren 
Daten kommen. Und in der anderen Funktion fragst du dann ab, ob dieses 
Flag nicht gesetzt ist.

Entworren heißt das:

Wenn das Dateiende erreicht ist, soll das Programm weiterlesen.


Hans M. schrieb:
> Hat jemand eine Idee, wo mein Fehler liegen könnte, ich kann leider den
> ganzen Code nicht posten, weil das bei der Abgabe mit Plagiatgefahr
> enden könnte!

Hä? Das verstehe ich nicht.

von Hans M. (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Hä? Das verstehe ich nicht.

Nein, ich hab mich da schlecht ausgedrückt. Mein komplettes Programm 
brauche ich für eine Hausübung und dort wird streng kontrolliert, ob 
nicht jemand was kopiert hat mit Plagiattestprogramm und deswegen kann 
ich so nicht den ganzen Code posten!

http://www.cplusplus.com/reference/clibrary/cstdio/feof/
A non-zero value is returned in the case that the End-of-File indicator 
associated with the stream is set.

Hier steht ja, wenn feof ungleich Null ist, dann ist das Ende des File 
erreicht.

>> if(feof(input_file) != 0) // hier will ich eigentlich herauskommen
>>   {
>>     *no_more_entries_flag = 0;
>>     return first_element_of_list;
>>   }

hier mache ich ja auch nichts anderes, sofern feof ungleich Null ist, so 
springe aus der Header-Einlesefunktion heraus und hör auf zum Einlesen.




Bin gerade draufgekommen, dass, egal wie viele Einträge ich einlese, am 
Ende jedes Mal bei mir ein Segmentation Fault kommt beim Ausführen!
Hab nun mit Valgrind getestet und hab folgenden Fehler bekommen:

==17148== 1 errors in context 1 of 2:
==17148== Invalid read of size 4
==17148==    at 0x8048910: freeStringOfList (ass3.c:417)
==17148==    by 0x804860B: main (ass3.c:229)
==17148==  Address 0x1c is not stack'd, malloc'd or (recently) free'd

D.h. ich greife auf einen Speicherbereich zu, auf den ich nicht 
zugreifen sollte, aber ich finde einfach nicht den Fehler.
Könnte ich jemanden einmal den Code zeigen, damit er sich das einmal 
anschaut, ich komme einfach nicht auf den Fehler drauf :/

von Rolf M. (rmagnus)


Lesenswert?

Hans M. schrieb:
> Hier steht ja, wenn feof ungleich Null ist, dann ist das Ende des File
> erreicht.
>
>>> if(feof(input_file) != 0) // hier will ich eigentlich herauskommen
>>>   {
>>>     *no_more_entries_flag = 0;
>>>     return first_element_of_list;
>>>   }
>
> hier mache ich ja auch nichts anderes, sofern feof ungleich Null ist, so
> springe aus der Header-Einlesefunktion heraus und hör auf zum Einlesen.

Was ist denn genau die Bedeutung des no_more_entries_flag, und warum 
setzt du es auf 0? In deiner while-Schleife in der übergeordneten 
Funktion bleibst du doch solange drin, bis es nicht mehr 0 ist.

> Bin gerade draufgekommen, dass, egal wie viele Einträge ich einlese, am
> Ende jedes Mal bei mir ein Segmentation Fault kommt beim Ausführen!
> Hab nun mit Valgrind getestet und hab folgenden Fehler bekommen:
>
> ==17148== 1 errors in context 1 of 2:
> ==17148== Invalid read of size 4
> ==17148==    at 0x8048910: freeStringOfList (ass3.c:417)
> ==17148==    by 0x804860B: main (ass3.c:229)
> ==17148==  Address 0x1c is not stack'd, malloc'd or (recently) free'd
>
> D.h. ich greife auf einen Speicherbereich zu, auf den ich nicht
> zugreifen sollte,

Ja, und zwar in Zeile 417 von ass3.c.

von Hans M. (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Bedeutung des no_more_entries_flag,

in der ReadHeader-Funktion habe ich folgende if
1
  if(feof(input_file) != 0)
2
  {
3
    *no_more_entries_flag = INITIALISATION_CONSTANT_ZERO; 
4
    return first_element_of_list;  
5
  }
also es wird gesetzt, sobald feof ungleich Null ist, also sobald das 
Ende des files erreicht wurde.

von Karl H. (kbuchegg)


Lesenswert?

> also es wird gesetzt,

Was wird da gesetzt?

Tu dir selbst einen Gefallen und achte darauf, wie deine Variablen 
heißen. Und dann geh konsequent nach dem Muster vor:

0  bedeutet falsch (false), also die Bedingung die durch die Variable
   ausgedrückt wird, ist NICHT erfüllt

1  bedeutet wahr (true). Die Bedingung ist erfüllt.

Damit KANN das hier nicht so lauten
1
  if(feof(input_file) != 0)
2
  {
3
    *no_more_entries_flag = INITIALISATION_CONSTANT_ZERO; 
4
    return first_element_of_list;  
5
  }

Wenn eof vorliegt, dann gibt es offensichtlich keine Einträge mehr. "No 
More Entries" muss daher TRUE sein. 0 ist aber nicht TRUE.

Und benutz nicht solche Makros wie INITIALISATION_CONSTANT_ZERO. Wenn 
ein Flag nur TRUE oder FALSE sein kann (und das sind Flags nun mal, 
sonst würden sie nicht Flags heißen), dann schreib das auch so
1
  if(feof(input_file))
2
  {
3
    *no_more_entries_flag = TRUE;
4
    return first_element_of_list;  
5
  }

Jetzt ist das logisch: Wenn das File zu ende ist (feof liefert wahr), 
dann gibt es keine Einträge mehr.

von Karl H. (kbuchegg)


Lesenswert?

Und du solltest tunlichst hier
1
  while(!(no_more_entries_flag))
2
  {  
3
    first_element_of_list = readHeader(input_file, 
4
      error_code, &no_more_entries_flag);
5
6
  ...

nach dem Leseversuch sowohl first_element_of_list UND 
no_more_entries_flag auswerten. first_element_of_list kann NULL sein. Du 
setzt es hier
1
  if((fread(&(first_element_of_list->header), sizeof(Header),
2
    1, input_file)) != 1)
3
  {
4
    first_element_of_list = NULL;
5
    return first_element_of_list;
6
  }

auf NULL. Du setzt aber NICHT error_code! Den würdest du zwar in der 
aufrufenden Funktion abfragen, setzt es aber nie.

Summa summarum: Du hast da ein heilloses Durcheinander mit Fehlercode?, 
gibt es noch Elemente?, konnte gelesen werden?.

Vereinfache das! Mach EINEN Fehlermechanismus und zieh den durch! Wenn 
die readHeader Funktion bei einem Fehler NULL zurückliefert, dann ist 
das ok. Der Aufrufer bricht dann einfach seinerseits die Leseschleife 
ab:

So ungefähr:
1
  while( header = readHeader(input_file, header) ) {
2
    readContent( input_file, header );
3
    number_of_entries++;
4
  }
5
6
  closeFile(&input_file);

einfach, simpel, überschaubar. Leider hast du nicht genug von deiner 
Liste gezeigt, so dass ich nicht sagen kann, wie das einhängen in die 
Liste funktioniert. Gefühlsmässig würde ich sagen: readHeader ist der 
falsche Ort dafür. Denn ein neuer Knoten wird nur dann eingehängt, wenn 
sowohl Header als auch Content korrekt gelesen werden konnte. Von daher 
müsste das einhängen hier irgendwo
1
  while( header = readHeader(input_file, header) ) {
2
    if( readContent( input_file, header ) ) {
3
      // <--- neuen Knoten header an dieser Stelle in die Liste einhängen
4
      number_of_entries++;
5
    }
6
  }
7
8
  closeFile(&input_file);
erfolgen, wenn sowohl der Header als auch der Content korrekt gelesen 
werden konnte. Da sieht man aber in deinem Code nichts davon, so dass du 
entscheiden musst wie das gehen soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.