Per fscanf Daten in struct einlesen



  • Hallo zusammen,

    ich wende mich mit einem Problem an euch, das mir schon länger Ärger bereitet und ich einfach keine Lösung hinbekomme. Ich habe bestimmt jetzt schon 10 stunden lang dieses scheinbar winzige Problem gegoogled, aber es ist keine Besserung in Sicht.
    Vielleicht schafft ihr es ja mir zu helfen, Danke schonmal im Vorraus!

    Also ich programmiere in C, mit Hilfe des Programms Eclipse. Ich bin neu in der Thematik und somit könnt ihr mir allemöglichen Tips und Tricks/Korrekturen und Anregungen sofort und unverblümt um die Ohren hauen.

    Meine Aufgabenstellung ist folgende:
    Ich habe eine Liste mit 44 Präsidenten der USA und soll die nach Vor- und Nachnamen mit Hilfe des Quicksort-Algorithmus sortieren. Ich habe die Vor- und die Nachnamen in einer Text-Datei vorliegen, jeweils für Vor- und Nachname 20 Characters in der Liste. Das Einlesen der Daten soll ausdrücklich mit dem fscanf-Befehl erfolgen, ich möchte also bitte keine Tipps haben, wie man es mit anderen Befehlen besser machen könnte.

    Mein Problem ist, ich kriege die Daten nicht in eine Datenstruktur struct eingelesen. Bei der Ausgabe in der Konsole stelle ich fest, dass sich das Komma zwischen Vor- und Nachname in der Liste pro Zeile einen Leerschritt nach links verschiebt und so dann bald schon den ersten Vornamen "zerstört" und ein unheimliches Chaos nach sich zieht.

    Ich werde zuerst meinen Code posten, dann einen Auschnitt, wie die Liste der Präsidenten aufgebaut ist und dann noch ein "Bild", das darstellt, wie die fehlerhafte Ausgabe bei mir in der Konsole aussieht:

    #include <stdio.h>
    #include <stdlib.h>
    /*--------------------Variablendeklaration---------------------------------------------------------------------*/
    typedef struct{
    	char vorname[21];
    	char nachname[21];
    	int position;
    }spresident;
    int i,zeilenzahl=1;
    char Dateiname[30],c;
    int abfrage;
    spresident *president;
    
    int main(void) {
    	/*-------------------------------------------------------------------------------------------------------------*/
    	/*--------------------1. Beginn Hauptprogramm------------------------------------------------------------------*/
    	/*-------------------------------------------------------------------------------------------------------------*/
    	/*--------------------1.1 Einlesen der Daten in die Struktur-Arrays-----------------------------------------------------*/
    	while (1){
    	/*-------------------------------------------------------------------------------------------------------------*/
    	printf("\n-----Bitte geben Sie den Namen der Datei an, aus der die Daten gelesen werden sollen:-----\n");
    	printf("-----zum Beispiel:inputdaten.txt-----\n");
    	scanf("%s",Dateiname);
    	printf("-----Der Name der Einlesedatei lautet: %s-----\n\n",Dateiname);
    	/*-------------------------------------------------------------------------------------------------------------*/
    	FILE *input;
    	input = fopen(Dateiname,"r");
    	if (input!=NULL){
    		//Anzahl Präsidenten
    		while ((c=fgetc(input))!=EOF){
    			if(c=='\n') {zeilenzahl++;}
    		}
    
    		rewind(input);
    		printf("Anzahl an Präsidenten: %d.\n",zeilenzahl);
    		//Speicherallokierung
    		president = (spresident*)malloc(zeilenzahl*sizeof(spresident));
    			if (president==NULL){
    				printf("Es konnte kein Speicherplatz für die Daten reserviert werden.");
    			}
    		/*--------------------!!!Relevanter Ausleseteil!!!------------------------------------------------------------------*/
    		//Werte zuweisen mit fscanf-Funktion.
    		for (i=0;i<zeilenzahl;i++){
    				fseek(input,i*41L,SEEK_SET);					//==>ist unerheblich, ob eingesetzt oder nicht...warum auch immer...
    				fscanf(input,"%20c",president[i].vorname);	//==>lese 20 character ein
    
    				fseek(input,1L,SEEK_CUR);						//==>überspringe einen character, um zum nachnamen zu gelangen
    				fscanf(input,"%20c",president[i].nachname);
    				fseek(input,1L,SEEK_CUR);						//==>überspringe einen character und damit in die nächste zeile
    
    				printf("%s,%s\n",president[i].vorname,president[i].nachname);
    				}
    		/*--------------------!!!Relevanter Ausleseteil(Ende)!!!------------------------------------------------------------------*/
    			fclose(input);
    	}
    	/*--------------------1.4 Aufgaben in Falle einer nicht vorhandenen Datei--------------------------------------*/
    		else {
    			printf("Datei konnte nicht geöffnet werden.\n");
    			printf("Drücken Sie die 1, um einen neuen Dateinamen einzugeben, oder die 2, um das Programm zu beenden.\n");
    			scanf("%d",&abfrage);
    			if (abfrage==1){
    				continue;
    			}
    			else if (abfrage==2) {
    				break;
    			}
    			else {
    				break;
    			}
    		}
    	/*-------------------------------------------------------------------------------------------------------------*/
    	break;
    	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    	//Ende Hauptprogramm mit While-Schleife.
    	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    	}
    	return EXIT_SUCCESS;
    }
    

    Ausschnitt Liste der Präsidenten:

    Gerald               Ford                
    Jimmy                Carter              
    Ronald W.            Reagan              
    George H. W.         Bush
    

    Ausgabe in der Konsole:

    George              ,Washington          
    John              ,  Adams             
    Thomas          ,    Jefferson       
    James         ,      Madison       
    James       ,        Monroe      
    John Quinc,          Adams
    

    Eigentlich liegen zwischen den ausgegebenen Zeilen immer noch (warum auch immer?) zwei Leerzeilen, das habe ich jetzt aber aus Gründen der Übersichtlichkeit ausgespart.

    Kann mir jemand sagen, warum sich dieses Komma da verschiebt und am Ende alles zerstört? Benutze ich den fscanf-Befehl überhaupt auf die korrekte Art und Weise? Nur um das nochmal klarzustellen, ich muss/will den fscanf-Befehl nutzen (ein Kollege hat ihn auch ähnlich benutzt und bei ihm hat es funktioniert.)

    Vielen Dank für die Bemühungen.

    Freundliche Grüße 🙂



  • Nimm doch einfach solch ein format:
    [cdoe]
    vorname1<whitespaces>nachname1
    vorname2<whitespaces>nachname2
    [/code]
    Du kannst dafür z.B. fgets benutzen um eine zeile zu lesen und dann mit sscanf das ganze auslesen:

    char line[256];
    fgets(input, line, 256);
    char vorname[32], nachname[32];
    sscanf(line, "%31s%31s", vorname, nachname);
    


  • @roflo

    roflo schrieb:

    Du kannst dafür z.B. fgets benutzen um eine zeile zu lesen und dann mit sscanf das ganze auslesen:

    solareclipse schrieb:

    Das Einlesen der Daten soll ausdrücklich mit dem fscanf-Befehl erfolgen, ich möchte also bitte keine Tipps haben, wie man es mit anderen Befehlen besser machen könnte.

    @solareclipse
    Wieso benutzt du globale Variablen?

    if (input!=NULL){
            //Anzahl Präsidenten
            while ((c=fgetc(input))!=EOF){
                if(c=='\n') {zeilenzahl++;}
            }
    

    Du überprüfst zwar ob input NULL ist, brichst in einem solchen Fall nicht ab.

    if (president==NULL){
                    printf("Es konnte kein Speicherplatz für die Daten reserviert werden.");
                }
    

    Hier auch, an dieser Stelle das Programm beenden.

    fscanf(input,"%20c",president[i].vorname);  //==>lese 20 character ein
    

    Hier hast du den falschen formatspecifier.



  • Übrigens, warum erstellst du einen Zeiger von der Struktur? Du hast in deinem Programm doch keine Funktionsübergabe.



  • Trotzdem find ich diesen Kram mit den 20 Zeichen etc merkwürdig. Wenn nur fscanf verwendet werden soll dann lass den part mit fgets halt raus.



  • Dein Problem sind die Leerzeichen in den Vornamen.

    Bei %20c liest scanf keinen C-String ein. D.h er wird nicht Nullterminiert.
    Dann kannst du den text auch nicht mit printf und %s ausgeben.

    Das mit den 2 Leerzeilen bei der Ausgabe hat damit zu tun. Du liest den Zeilenumbruch ein und speicherst ihn mit ab.

    Wenn Leerzeichen in den Daten vorkommen, sind sie ein denkbar schlechtes Trennzeichen.



  • Du könntest den Nachnamen mit

    char nachname[21], punkt;
    ...
    do {
      fscanf(datei, "%20s%c", nachname, punkt);
      if (punkt == '.') {
        nachname an vorname anhängen;
      }
    } while (punkt == '.')
    

    einlesen.

    Wenn punkt ein '.' ist, gehört der Teil noch zum Vornamen. Sonst ist es der Nachname



  • - globale Variablen sind Müll
    - deine Zeilen-Zahlaktion ist auch Müll (was passiert, wenn die letzte Zeile kein '\n' besitzt?)
    - "%20c" ist auch Müll, da kein '\0' angehängt wird
    - deine fseekerei sieht furchtbar aus
    - wenn du fscanf einsetzen sollst, warum liest du dir die Spezifikation dafür nicht vollständig durch?

    typedef struct{
        char vorname[22];
        char nachname[22];
        int position;
    }President;
    
    while( 2==fscanf(f,"%21[^\n]%21[^\n] ",president[i].vorname,president[i].nachname) )
    {
      ...
    }
    


  • - globale Variablen sind Müll
    - deine Zeilen-Zahlaktion ist auch Müll (was passiert, wenn die letzte Zeile kein '\n' besitzt?)
    - "%20c" ist auch Müll, da kein '\0' angehängt wird
    - deine fseekerei sieht furchtbar aus
    - wenn du fscanf einsetzen sollst, warum liest du dir die Spezifikation dafür nicht vollständig durch?

    -EDIT: Ich habe es getestet, wenn ich die Variablen in die main-Funktion packen will, will Eclipse immer den debug-modus öffnen und das Programm lässt sich gar nicht mehr ausführen...Woran liegt das?
    -sry habe nix besseres gefunden im Netz und mir ist auch nix besseres eingefallen, bin ja noch ein newbie
    -Was bedeutet das, "es wird kein '\0' angehängt?
    -ja das sieht tatsächlich sehr unprofessionell aus:)
    -woher soll ich wissen, was all diese 'specifiers' für eine konkrete Bedeutung haben? Es gibt nicht für all diese specifiers ein Beispiel, deswegen ist es oft schwer aus den Online-Bibliotheken schlau zu werden...

    @Wutz:
    Bitte erklär mir, was:
    1. die Bedingung "2==fscanf(...)" zu bedeuten hat.
    2. der specifier "[^\n]" denn nun bedeutet, ich kapiere die Erklärung in der Online-Bib nicht...
    3. wieso du für vor- und nachname auf einmal 22 Characters nimmst? Ich dachte man muss immer nur 1 extra nehmen?

    So hat es jetzt auch geklappt:

    for (i=0;i<zeilenzahl;i++){
    
    				fscanf(input,"%20c20c\n",president[i].vorname,president[i].nachname);	
    
    				president[i].position=i;
    				printf("%s,%s (%d.Präsident)\n",president[i].nachname,president[i].vorname,president[i].position+1);
    		}
    

    Das ist ja jetzt nicht viel anders, als das was du vorgeschlagen hast Wutz...
    Die Ausgabe sieht jetzt so aus:

    Washington          ,George               (1.Präsident)
    Adams               ,John                 (2.Präsident)
    Jefferson           ,Thomas               (3.Präsident)
    Madison             ,James                (4.Präsident)
    


  • scanf ist standardisiert.
    Die Specifier für scanf sind standardisiert

    Das Verhalten davon ist genau beschrieben. Da hat sich seit 1989 wenig dran geändert (es sind ein paar dazu gekommen).

    http://de.cppreference.com/w/c/io/fscanf

    Da steht die Bedeutung der Formatspecifier und auch die des Rückgabewertes.

    Weißt du, was ein C-String ist, bzw. wie in C String verarbeitet werden? Das sind absolute Grundlagen.
    Lies es nochmal nach. Evtl. findest du ein Beispiel für die Funktion strlen .




Anmelden zum Antworten