Problem beim Einlesen einer .txt in C

RexRayne

Grünschnabel
Hallo Leute,

Ich habe ein Problem beim Einlesen einer Textdatei, die folgendermaßen aussieht:
0;100;0;0;
0.9;100;0;600;Oranienburg
2.5;100;0;0;Abzweig_Lehnitz
2.8;120;0;0;
5.8;120;0;0;Bk_Borgsdorf

Mein gegenwärtiger Programmcode sieht so aus:

C++:
#include <stdio.h>
#include <stdlib.h>

struct Datensatz{
float Kilometer;
float Geschwindigkeit;
float Neigung;
float Wartezeit;
char Ort[20] ;
                 };
int main(int argc, char *argv[]) {
   
struct Datensatz Input[100];   
FILE *Textdatei;
int i,gefunden;
char name[20];

gefunden=0;
while (gefunden==0){
    printf("Welche Datei wollen Sie zum Lesen öffnen: ");
   scanf("%s",name);
   Textdatei=fopen(name,"r");
   if (Textdatei==NULL){
    printf("Datei konnte nicht geladen werden.\n"); 
  }
  else
  {
      gefunden=1;
  }
}
  i=0;
  while((fscanf(Textdatei,"%f;%f;%f;%f;%s\n", &Input[i].Kilometer,&Input[i].Geschwindigkeit,&Input[i].Neigung,&Input[i].Wartezeit,Input[i].Ort))!=EOF)
  {
      printf("Input: %f\n", Input[i].Kilometer);
      printf("Input: %f\n", Input[i].Geschwindigkeit);
      printf("Input: %f\n", Input[i].Wartezeit);
          printf("Input: %f\n", Input[i].Neigung);
      printf("Input: %s\n", Input[i].Ort);
      i++;   
  }
  fclose(Textdatei);
    return 0;
}

Die dazugehörige Ausgabe sieht so aus:

Input: 0.0000
Input: 100.000
Input: 0.0000
Input: 0.0000
Input: 0.9;100;0;600;Oranienburg
Input: 2.50000
Input: 100.000
Input: 0.00000
Input: 0.00000
Input: Abzweig_Lehnitz
Input: 2.80000
Input: 100.000
Input: 0.00000
Input: 0.00000
Input: 5.8;120;0;0;Bk_Borgsdorf

Mein Problem liegt nun darin, dass an einigen Stellen der Textdatei an letzter Stelle manchmal ein Ort steht und manchmal nicht, an den Stellen wo keiner steht, wird bei dem Input.Ort die gesamte nächste Zeile eingelesen. Meine Frage nun, wie kann ich das verhindern? An der Textdatei selbst kann ich leider nichts ändern die ist so festgeschrieben. Ich wäre wirklich dankbar für Hilfe und Ratschläge.

Grüße Rex
 
Hallo RexRayne und herzlich willkommen!

Zuerst zum eigentlichen Problem: Da deine Zeilen unterschiedliche Argumentlängen haben (mal nur 4 floats, mal 4 floats + 1 String), kann fscanf nicht wissen, wann fertig sein soll - und liest nach der ersten Zeile schlicht weiter, sodass die 2. Zeile als ganzer String betrachtet und entsprechend gespeichert wird. Das hast du ja selbst gut erkannt.
Da du das Speicherformat nicht anpassen kannst, wirst du wohl fgets() nutzen müssen, damit du die Datei zeilenweise auslesen kannst. Dann kannst du den Buffer, den du mit fgets() gefüllt hast, mit sscanf() weiterverarbeiten. Ich würde dir zudem empfehlen, auf den Rückgabewert von *scanf zu achten, um zu sehen, wieviel überhaupt korrekt gelesen wurde.

Falls du ein Performancefreak bist: fread() verwenden, um die Datei byteweise zu lesen. Bei kleinen Dateien kannst du gleich die gesamte Datei lesen (dazu: fseek und ftell, um die Dateigrösse zu bestimmen). Dann kannst du die Datei per strtok o.ä. durchpflügen.

Zu deinem Code im Allgemeinen:
C++:
char name[20];
//...
scanf("%s",name);
ist brandgefährlich. 19 Zeichen sind für Dateinamen nicht viel, und wenn mehr als 20 Zeichen gelesen werden, ist das Verhalten undefiniert. Daher
C++:
scanf("%19s", name);
verwenden.

C++:
while (gefunden==0){
//...
gefunden = 1;
}
Warum benutzt du nicht die Schleifenkontrollelemente wie break?

C++:
printf("Input: %f\n", Input[i].Kilometer);
//...
Warum gibst du x-Mal dieselbe Kennung aus? Gerade für's Debugging wäre es doch angenehmer, bei einer Ausgabe anzugeben, welcher Input nun gemeint ist.
Also z.B.
C++:
printf("Input (km): %f\n", Input[i].Kilometer);

Gruss
cwriter
 
Zuletzt bearbeitet:
Hallo cwriter,

danke für die schnelle Antwort, mit der Hilfe weiß ich nun wie ich das Problem beheben kann. Wenn das Programm dann fertig ist, lade ich den Quellcode nochmal hoch, vielleicht hilft es auch anderen Leuten mit ähnlichen Problemen.

Außerdem noch ein dickes Lob an dich, für die vielen Ratschläge und Verbesserungen. Ich bin kein C-Profi und finde solche Hinweise sehr gut, damit man sich verbessern kann. Die Verbesserungen werde ich auch gleich mit umsetzen.
Danke nochmal

Grüße Rex
 
Zurück