C Per fscanf Daten in struct einlesen

solareclipse

Cadet 2nd Year
Registriert
Nov. 2015
Beiträge
19
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:

C:
#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:

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

Ausgabe in der Konsole(Beachtet nicht die Nummern am linken Rand, nur so bleibt die Formatierung enthalten):

HTML:
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?

Vielen Dank für die Bemühungen.

Freundliche Grüße :)
 
Versuch mal, deine Namen nach dem Einlesen mit 0 zu terminieren. Der %c-Modus von fscanf() macht das nicht automatisch. Also:

Code:
president[i].vorname[20] = '\0';  // für nachname entsprechend
 
solareclipse schrieb:
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.

OK, Du hast zeilenweise Records als Daten, da solltest Du auch die Daten zeilenweise lesen. Hier fscanf zu verwenden ist meiner Meinung nach falsch. Aber da Du ausdrücklich keine "anderen Tipps" haben möchtest, sage ich dazu nichts weiter, auch wenn es mir schwerfällt ;)
 
Lies die Vor- und Nachnamen der Präsidenten in einem Rutsch ein, statt für Vor- und Nachname einen eigenen fscanf Aufruf zu machen. Bei der Gelegenheit kannst Du dann auch gleich noch den Zeilenumbruch mit einlesen.

Code:
for (i=0;i<zeilenzahl;i++) {
    fscanf(input,"%20c%20c\n", president[i].vorname, president[i].nachname);
    				
    printf("%s,%s\n", president[i].vorname, president[i].nachname);
}

Und nutze anstelle des malloc doch lieber calloc, das initialisiert Dir den Speicher schon mit 0, so dass Du kein \0 an die eingelesenen Texte anfügen musst.

Code:
president = (spresident*)calloc(zeilenzahl, sizeof(spresident));
 
Hi,

kommt mir bekannt vor, die Aufgabe. Du studierst aber nicht zufällig an der Uni Stuttgart (beim Rudolph)?
Falls doch wäre es nämlich noch gar nicht so schelcht, zu wissen, dass die Vor- und Nachnamen in der Eingabedatei Tabulator-getrennt sind.
Edit: Hatte ich falsch in Erinnerung, die ersten zwanzig Zeichen sind Vornamen und dann Leerzeichen.

In deinem Code passt auf jeden Fall was beim Einlesen nicht. Das ganze rumsgespringe an der Inputdatei stimmt einfach noch nicht. Wobei ich mir das sowieso sparen würde und erst einmal die komplette Datei einlesen und zwischenspeichern würde. Ich finde ja, dass sich dabei auch ein zeilenweises Array ganz gut eignet.
Und dann im Anschluss die Vor- und Nachnamen aus den Zeilen holen.
 
Zuletzt bearbeitet:
Lies die Vor- und Nachnamen der Präsidenten in einem Rutsch ein, statt für Vor- und Nachname einen eigenen fscanf Aufruf zu machen. Bei der Gelegenheit kannst Du dann auch gleich noch den Zeilenumbruch mit einlesen.

Den Zeilenumbruch "\n" mit in den fscanf-Befehl einzubauen war der Schlüssel! Warum auch immer...
Danke Balou72! Jetzt sieht die Ausgabe folgendermaßen aus:
HTML:
Washington          ,George               (1.Präsident)
Adams               ,John                 (2.Präsident)
Jefferson           ,Thomas               (3.Präsident)
Madison             ,James                (4.Präsident)
Monroe              ,James                (5.Präsident)
Adams               ,John Quincy          (6.Präsident)
Jackson             ,Andrew               (7.Präsident)

Jetzt kann ich also mit der eigentlichen Arbeit beginnen - dem Quick-Sort-Algorithmus:cool_alt:.

Du studierst aber nicht zufällig an der Uni Stuttgart (beim Rudolph)?
Falls doch wäre es nämlich noch gar nicht so schelcht, zu wissen, dass die Vor- und Nachnamen in der Eingabedatei Tabulator-getrennt sind.

Haha, ja ich bin wirklich beim Rudolph in der Vorlesung! Bist du auch ein Luft- und Raumfahrer?:)
Woher weist du, dass zwischen Vor- und Nachnamen ein Tabulator ist? In meiner Aufgabenstellung sehe ich davon nix...

Noch eine technische Frage @NullPointer:
Was verursacht die Null-Terminierung? Dass der Zeilenumbruchsbefehl beim Nachnamen beim Einlesen ausgespart wird?

Danke für all die Antworten!
 
Ein String ist normalerweise immer nullterminiert. Daran erkennt dann z.B. printf auch, dass der String zu Ende ist.
Da du nicht mit den Stringfunktionen arbeitest, sondern mit einzelnen chars, müsstest du danach manuell ein \0 einfügen.

Außerdem solltest du mit den Zeikenumbrüchen vorsichtig sein, da die je nach OS unterschiedlich gehandhabt werden (unter Windows besteht ein Zeilenumbruch aus CR und LF, unter Linux nur ein LF.)

Jep, ich studiere auch LRT. ich weiß daher, dass die Namen Tabulator-getrennt sind, weil wir nachgefragt haben. Zuerst hatte ich nämlich (relativ aufwendig) immer nach dem letzten Leerzeichen in der Zeile gesucht und dort getrennt.
 
Zurück
Oben