Hilfe bei eingabe in C (mit dynamischer Speicherreservierung)



  • Hallo!
    Ich verzweifel grad an einer Aufgabe und vielleicht kann mir ja hier jemand helfen. Ich soll in C ein Programm schreiben, bei dem über eine externe txt-Datei ("Konto.txt") Daten eingelesen werden sollen (mit fscanf()). In diesem Beispiel sieht die Datei z.B. so aus:

    Name:	Mueller		Kontostand:	1234.00
    Name:	Maier		Kontostand:	7893.44
    Name:	Huber		Kontostand:	453.04
    Name:	Scmitt		Kontostand:	1.65
    Name:	Stark		Kontostand:	-543.66
    

    Zuerst soll einfach das Gesamtguthaben addiert werden. Dafür hab ich folgendes Programm geschrieben:

    #include<stdlib.h>
    #include<stdio.h>
    
    typedef struct
    	{
    		char Name[20];
    		float Haben;
    	}Daten;
    
    int main(void)
    {
    	int i=0, rueckgabe=0;
    	float summe=0;
    	Daten *Konto;
    	FILE *fp;
    
    	fp=fopen("Konto.txt", "r");
    
    	if(fp == NULL)	//Test, ob Datei überhaupt vorhanden ist (nicht wichtig)
    	{
    		printf("Datei nicht vorhanden!\n"); return;
    	}
    
    	Konto = (Daten *)malloc(sizeof(Daten));
    
    	rueckgabe = fscanf(fp, "Name: %s Kontostand: %f", &Konto[i].Name, &Konto[i].Haben);
    
    	printf("%f   %d \n", Konto[i].Haben, rueckgabe); //(zur Kontrolle)
    
    	while(rueckgabe==2)
    	{
    		i++;
    
    		Konto = (Daten *)realloc(Konto, (i+1)*sizeof(Daten));
    
    		rueckgabe = fscanf(fp, "Name: %s Kontostand: %f", &Konto[i].Name, &Konto[i].Haben);
    		printf("%f   %d \n", Konto[i].Haben, rueckgabe); //zur Kontrolle
    
    	}
    
    	printf("i= %d \n",i); //zur Kontrolle
    
    	for(;i>=0;i--)
    	{
    		summe = summe + Konto[i].Haben;
    	}
    
    	printf("Gesamtkontostand: %f", summe);
    }
    

    Die Ausgabe lautet dann folgendermaßen:

    1234.000000   2 
    0.000000   0 
    i= 1 
    Gesamtkontostand: 1234.000000
    

    Also wird die erste Zeile eingelesen, es geht wohl auch in die while-Schleife rein, aber dann passiert nichts mehr.

    Testhalber habe ich die while-Schleife abgeändert, so dass ich von Hand selbst Daten eingeben kann:

    while(rueckgabe==2)
    	{
    		i++;
    
    		Konto = (Daten *)realloc(Konto, (i+1)*sizeof(Daten));
    
    		rueckgabe = scanf("Name: %s Kontostand: %f", &Konto[i].Name, &Konto[i].Haben);
    		printf("%f   %d \n", Konto[i].Haben, rueckgabe);
    
    	}
    

    Eingabe:

    Name: Gabi
    Kontostand: 100
    

    Ausgabe:

    1234.000000   2 
    100.000000   2 
    0.000000   0 
    i= 2 
    Gesamtkontostand: 1334.000000
    

    Die zweite Eingabe klappt also, jedoch kann ich danach nichts mehr Eingeben, es kommt sofort die 0-er-Ausgabe und die while-Schleife wird verlassen. Seltsam finde ich auch, das die erste Eingabe (also die Aus der Datei) nicht ausgegeben wird, BEVOR ich die Eingabe machen kann, sondern erst danach...

    Was mach ich falsch? Vor allem kann ich nicht verstehen, warum die while-Schleife gleich verlassen wird...
    Ich würde mich dabei gerne an dieses Grundgerüst halten, weil wir es so halt in der Uni machen (falls es geht).



  • Rückgabewerte von malloc und realloc werden nicht geprüft, aber da wird der Fehler vermutlich nicht liegen.
    Guck doch einfach mit dem Debugger was schief läuft 😕



  • Ich benutze eclipse, ich weiß, da gibts einen debugger, aber irgendwie werd ich aus dem nicht schlau, habs schonmal ausprobiert... 😞



  • Ganz schlechtes Design deines Codes, zuviel Code für ein einfaches Einlesen einer Textdatei.
    Außerdem ist die Verwendung von fscanf für Anfänger NICHT zu empfehlen, die kennen die Randbedingungen nicht.
    Lies die Datei zunächst zeilenweise in einen String (ohne fscanf) und werte dann diesen String für jede Zeile aus.



  • Hm..
    auf jeden Fall liegt der Fehler bei fscanf in Zeile 36, ich dachte ja weil das "&" vor dem Konto[i].Name falsch ist, aber das ändert lustigerweise gar nichts 😕
    Stehe da gerade auch etwas auf dem Schlauch, obwohl der Fehler vermutlich ganz einfach ist 😃



  • ich wäre ja schon einen Schritt weiter, wenn ich verstehen würde, warum es auch bei "scanf()" nicht funktioniert...
    Es gibt doch als "rueckgabe" eine 2 aus, also müsste man doch in der while-Schleife bleiben...



  • kratob schrieb:

    Es gibt doch als "rueckgabe" eine 2 aus, also müsste man doch in der while-Schleife bleiben...

    Nein, Rückgabe ist 0 nach dem ersten fscanf in der Schleife.



  • also ich hab jetzt von der abgeänderten Version gesprochen, in der ich "von Hand" was eingeben kann:
    erstmal ist "rueckgabe" = 2, dann geht es wohl auch wieder in die Schleife rein, lässt mich aber nichts mehr eingeben, sondern liefert sofort als "rueckgabe" 0 zurück und verlässt die schleife.
    stimmt doch, oder? aber wieso wartet es nciht, bis ich wieder was eingegeben hab?



  • Weil das '\n' noch im Puffer ist.
    Lies die Zeile mit fgets ein und werte diese dann mit scanf aus. So kannst du sicher sein immer eine Zeile neue zu lesen.



  • Mach bei fscanf einfach ein Leerzeichen ganz ans Ende des Format Strings, dann läufts.



  • Ok, also das Problem scheint ja wirklich zu sein, das die Zeile in meiner Datei nicht weiter gewechselt wird.
    ich habs mal so versucht. fgets() kannte ich vorher halt noch nicht, deswegen brauch ich da wohl auch noch ein wenig Hilfe.

    Hier mein neues Programm:

    int main(void)
    {
    
    	char *zeile; //NEU
    	int i=0, rueckgabe=0;
    	float summe=0;
    	Daten *Konto;
    	FILE *fp;
    
    	fp=fopen("Konto.txt", "r");
    
    	if(fp == NULL)	//Test, ob Datei überhaupt vorhanden ist
    	{
    		printf("Datei nicht vorhanden!\n"); return;
    	}
    
    	fgets(zeile, 1000, fp); //NEU
    
    	Konto = (Daten *)malloc(sizeof(Daten));
    
    	rueckgabe = scanf(zeile, "Name: %s Kontostand: %f", &Konto[i].Name, &Konto[i].Haben);
    
    	printf("%f   %d \n", Konto[i].Haben, rueckgabe); //(zur Kontrolle); Bei der Ausgabe MUSS ein Stern vor den Zeiger --> "Name" (bei Eingabe MUSS die Adresse übergeben werden)!
    
    	while(rueckgabe==2)
    	{
    		i++;
    		fgets(zeile ,1000 ,fp); //NEU
    
    		Konto = (Daten *)realloc(Konto, (i+1)*sizeof(Daten));
    
    		rueckgabe = scanf(zeile, "Name: %s Kontostand: %f", &Konto[i].Name, &Konto[i].Haben);
    		printf("%f   %d \n", Konto[i].Haben, rueckgabe);
    
    	}
    
    	printf("i= %d \n",i);
    
    	for(;i>=0;i--)
    	{
    		summe = summe + Konto[i].Haben;
    	}
    
    	printf("Gesamtkontostand: %f" , summe);
    }
    

    Der Teil vor der while Schleife funktioniert wohl schonmal. Nachdem ich fgets() auch in der while-Schleife geändert habe, gibt eclipse beim starten des Porgramms (Compiliert wird ohne Fehlermeldung) folgende Fehlermeldung aus (in rot):

    6 [main] test_C-Projekt 4972 exception::handle: Exception: STATUS_ACCESS_VIOLATION
       1197 [main] test_C-Projekt 4972 open_stackdumpfile: Dumping stack trace to test_C-Projekt.exe.stackdump
    

    Habe ich fgets richtig angewandt? oder müsste es richtig heißen:

    fgets(zeile+i ,1000 ,fp)
    

    ?
    Und wie funktioniert hier genau der Zeilewechsel? "Merkt" sich das Programm, dass beim einlesen der ersten Zeile am ende ein '/n' stand? Oder muss ich noch was tun, damit in der Schleife jedesmal die Zeile gewechselt wird?



  • sorry, ich hab natürlich den Teil vor der main-Fkt so wie vorher:

    #include<stdlib.h>
    #include<stdio.h>
    
    typedef struct
    	{
    		char Name[20];
    		float Haben;
    	}Daten;
    
    int main(void)
    {
    ...
    

    daran lag die Fehlermeldung also nicht



  • Du musst Speicher für "Zeile" reservieren oder ein Array draus machen. zB:

    char buf[200];
    fgets(buf, sizeof(buf), stdin);
    

    "Merkt" sich das Programm, dass beim einlesen der ersten Zeile am ende ein '/n' stand?

    1. fgets() liest bis einschließlich '\n'
    2. Der filestream merkt sich bis wohin du gelesen hast.

    3.

    cooky451 schrieb:

    Mach bei fscanf einfach ein Leerzeichen ganz ans Ende des Format Strings, dann läufts.



  • @ cooky451

    hey vielen dank, du bist ja klasse! Das mit den Leerzeichen funktioniert 🙂
    Aber woran liegt das?



  • ok, ich glaube ich kanns selbst erklären:
    Ohne Leerzeichen hört das Programm nach der letzten Variable auf zu lesen. Mit Leerzeichen liest es auch das '\n' mit, gell?

    Mit fgets() bekomm ichs nicht hin. Ist aber nicht so schlimm, hätte mich nur mal interessiert, da hier so viele meinen, das es damit besser wäre.



  • Ehrlich gesagt kann ich mir selbst nicht so ganz erklären warum es mit einem Leerzeichen funktioniert, das war nur so eine Ahnung. Eigentlich sollte scanf eh alle '\n' ignorieren. Vielleicht weiß hier ja jemand mehr, ich nutze eigentlich auch immer nur fgets..

    Mit fgets musst du dann sscanf benutzen. In etwa so:

    typedef struct
    {
      char name[20];
      float credit;
    } ACC_DATA;
    
    int main()
    {
      int i, retval = 2;
      float amount = 0;
      ACC_DATA *account = 0;
      FILE *file;
    
      file = fopen("test2.txt", "r");
      if(!file)
        exit(-1);
    
      for (i = 0; retval == 2; ++i)
      {
        char buf[0x100];
        if (!fgets(buf, sizeof(buf), file))
          break;
        account = (ACC_DATA*)realloc(account, (i + 1) * sizeof(*account));
        if (!account)
          exit(-2);
        retval = sscanf(buf, "Name: %19s Kontostand: %f", account[i].name, &account[i].credit);
        printf("Name: %s; Guthaben: %f\n", account[i].name, account[i].credit);
      }
    
      fclose(file);
    
      for(;i>=0;i--)
        amount+= account[i].credit;
    
      printf("Gesamtkontostand: %f\n", amount);
    
      free(account);
      system("pause");
      return 0;
    }
    


  • kratob schrieb:

    Hier mein neues Programm:

    Immer noch Müll. Du hast die Hinweise nicht gelesen/umgesetzt.
    Das wird heute nichts mehr mit dir.



  • Wo ist das Prob, das Programm läuft doch jetzt, dank dem Hinweis von cooky451.

    Ich glaube dir ja, das es vielleicht auch anders und einfacher geht, aber ich kann es nunmal nur so machen, wie ich es bis jetzt im Studium gelernt hab...
    Kanns immer noch nicht glauben, dass ich seit Freitag da rummach und am Ende lag es an einem Leerzeichen... ^^



  • Das Leerzeichen in scanf steht für sog. 'Whitespace' Das sind Leerzeichen, Tabulatoren und Zeilenvorschübe.
    Möglicherweise steht in der Datei nach dem Kontostand noch ein Leerzeichen.



  • nein, hab ich schon getested


Anmelden zum Antworten