C Zahlen aus formatierten Textdateien extrahieren

Stannis

Lieutenant
Registriert
Juli 2011
Beiträge
575
Hi.
Ich muss mit C eine formatierte Textdatei analysieren. Formatiert heißt, dass die in der Datei enthaltenen Zahlen in einer gewissen Reihenfolge stehen, so etwa:
Code:
"Text am Kopf der Datei"

100;94;07 3.949 2.73
94;11;44 4.88 9.72

Ich muss diese Zahlen verarbeiten. Mit C an sich bin ich recht fit, aber an der Dateiverarbeitung happert's hier ein bisschen.
Ich habe schon versucht, mit sowas wie

Code:
fscanf(fp, "%d;%d;%d %f %f", &a, &b, &c, &d, &e);

voran zu kommen, aber da kommt nur Blödsinn raus (-2 Milliarden und sowas), vermutlich weil die Dateiverarbeitung erstens mal den Text am Dateikopf mitauswertet, zweitens vermutlich weil das Programm mit dem Zeilenumbruch nicht klar kommt.

Die Standardfunktion für zeilenweises Lesen fgets bringt mich auch nicht wirklich weiter, dadurch verlagere Ich das Auswertungsproblem nur in einen String.

Wie mache Ich das? Muss doch ganz einfach gehen, Dateien auswerten ist doch was ganz Alltägliches :/
 
Also ich würde mich hier mit strtok durchhangeln und dann die einzelnen Teile casten.

Hier mal ein Ansatz um den String aufzudröseln..

Code:
char string[] = "100;94;07 3.949 2.73";
	char delimiter[] = ";";
	char *ptr;
	
	ptr = strtok(string, delimiter);
	
	while(ptr != NULL) {
		printf("Teil: %s\n", ptr);
                // ptr zu Int oder float casten
                int number1 = atoi(ptr);
		// naechster Teil lesen
	 	ptr = strtok(NULL, delimiter);
               // Bei drittem Element wieder aufsplitten mit Blank " "...usw
	}
 
Nun so sicher bin ich in C nicht, hab aber zufälligerweise C gerade in der Uni.
Ich würde mal laienhaft behaupten, dass die Feldtrenner ";" ein Problem darstellen. Die solltest du durch Freizeichen, oder Tabs ersetzen. Den Dateikopf mit den unnützen Strings würde ich entweder rauslöschen, oder mit "#" am Zeilenanfang markieren und mit einer Bedingung beim auslesen ausschließen.

Code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


int main ( void )
{
    int i;
    FILE *fp;
    char line[257];
    float x,y;

    if ( (fp=fopen("Datei.txt","r")) == NULL ) {
        printf("Die Datei konnte nicht geoeffnet werden\n");
        exit(0);
    }

    i = 0;
    while ( fgets(line,256,fp) != NULL )
    {
        if (line[0]=='#') {                         // Kommentarzeile ausgeben wenn mit # markiert
            printf("Lesen der Zeile %d war erfolgreich\n",i);
            printf("Die Kommentar-Zeile lautet:\n%s",line);
        }
        else {
            sscanf(line,"%f %f",&x,&y);
            printf("Wert X: %f, Wert Y: = %f\n",x,y);
        }
        i++;
    }

    fclose(fp);
    exit(0);
}

Natürlich an deine Bedürfnisse anpassen (Variablenanzahl) und dann müsste es passen.

Grüße
 
Wenn du weißt, wie viele Zeilen du am Anfang überspringen willst, dann würde das so gehen:
Code:
FILE *fp;
int a, b, c, i, linesToSkip = 2;
float d, e;

fp = fopen("file.txt", "r");

for (i=0; i<linesToSkip; i++)
{
    do
        c = fgetc(fp);
    while (c != '\n');
}

/* hier noch Schleife oder was du haben willst... */
fscanf(fp, "%d;%d;%d %f %f", &a, &b, &c, &d, &e);
printf("%d;%d;%d %f %f\n", a, b, c, d, e);

Hat bei mir als Ausgabe:
Code:
100;94;7 3.949000 2.730000
 
@powerfx & Houdey:
Danke, Eure Methoden funktionieren recht gut, wobei mir power's besser gefällt: Den Filepointer an die richtige Stelle bewegen und dann die Standardfunktionen benutzen.

@Woodz:
Ich hätte wohl dazusagen sollen, dass sowas nicht möglich ist; Ich kann nicht einfach von Hand die Datei bearbeiten, bzw. das ist ja nicht Sinn der Sache bei einer automatischen Auswertung. Die Datei hat mehrere hundert Einträge und wird von einem anderen Programm genau so generiert wie oben angegeben.
Ergänzung ()

Öhm, wo wir schon dabei sind:
Kann jemand einen Editor empfehlen, mit dem man Binärdateien ansehen kann?
 
Stannis schrieb:
Code:
fscanf(fp, "%d;%d;%d %f %f", &a, &b, &c, &d, &e);

voran zu kommen, aber da kommt nur Blödsinn raus (-2 Milliarden und sowas), vermutlich weil die Dateiverarbeitung erstens mal den Text am Dateikopf mitauswertet, zweitens vermutlich weil das Programm mit dem Zeilenumbruch nicht klar kommt.

Von der scanf-Funktionen *immer* den Rückgabewert checken.
 
Stannis schrieb:
Die Standardfunktion für zeilenweises Lesen fgets bringt mich auch nicht wirklich weiter, dadurch verlagere Ich das Auswertungsproblem nur in einen String.

Wie mache Ich das? Muss doch ganz einfach gehen, Dateien auswerten ist doch was ganz Alltägliches

Du nimmst immer fgets() bei realen Programmen, wenn die Daten zeilenweise vorkommen! Die Lösung ist genau das: "dadurch verlagere Ich das Auswertungsproblem nur in einen String". Genauer: "das Auswertungsproblem in eine Funktion für Strings verlagern":

Pseudo:
Code:
 if( fgets(buffer, sizeof(buffer), fh) != NULL ) {
   n = getnumbers_version_44(buffer, &zahlen); /* n = Anzahl o.k. übertragener Zahlen */
}
Für ähnliche Aufgaben nimmst Du die Funktion dann als Ausgangsbasis und schreibst daraus eine Neue.
 
Danke soweit zusammen.

@blöderidiot: Was soll getnumbers_version_44 sein; doch nicht etwa eine Standardfunktion?

So oder so sehe Ich in der String-Verarbeitung keinen großen Vorteil gegenüber fscan, Ich muss dann mit Funktionen den String aufdröseln und stehe wieder vor dem Problem, die Zahlen einzulesen.

nebenbei:
Hat jemand eine Idee, wieso es zum Speicherfehler kommt, wenn man einer Funktion einen falschen Dateinamen gibt? Die fopen() sollte dann einfach NULL zurückgeben und das Programm via if-Verzweigung beendet werden.
 
Zuletzt bearbeitet:
Ganz einfach: Mit scanf ist die Handhabung eines Fehlerfalles sehr kompliziert. Zeichen von stdin, die schon gelesen sind, aber nicht richtig interpretiert. Zeichen, die nicht interpretiert werden konnten und somit im file stream verbleiben, etc. Wenn du genaueres wissen willst, google einfach nach den Gründen, wieso scanf für interaktive Eingaben nicht gut ist. Das trifft nicht notwendigerweise auf sscanf und fscanf (aus Dateien, nicht von stdin) zu. Deshalb wurde hier auch ein einfacher Trick erwähnt: Stdin in einen Buffer einlesen und den parsen.
 
Stannis schrieb:
@blöderidiot: Was soll getnumbers_version_44 sein; doch nicht etwa eine Standardfunktion?

So oder so sehe Ich in der String-Verarbeitung keinen großen Vorteil gegenüber fscan, Ich muss dann mit Funktionen den String aufdröseln und stehe wieder vor dem Problem, die Zahlen einzulesen.

Hallo Stannis, es ist so gemeint, dass Du ja ein "allgemeines Problem" (Datei einlesen) und eine spezifische Anforderung (spezielles Zeilenformat) hast. Daher verlagerst Du alles Spezifische möglichst in austauschbare Funktionen, die Du bei Bedarf neu schreibst. Ein quick&dirty-Beispiel:
Code:
#include <stdio.h>

 struct Zahl_v01 { int x[3]; double y[2]; };  /* Problem */
 char *sfmt_v01 = "%d%*[;]%d%*[;]%d %lf %lf"; /* scan-Format  */
 char *pfmt_v01 = "%d\t%d\t%d\t|\t%g\t%g";      /* print-Format */

 int getnumbers_v01(char *pb, struct Zahl_v01 *pZ, char *s_fmt)
{
 return sscanf(pb, s_fmt, pZ->x+0, pZ->x+1, pZ->x+2, pZ->y+0, pZ->y+1);
}

 void printnumbers_v01(struct Zahl_v01 *pZ, char *p_fmt)
{
 printf(p_fmt, pZ->x[0], pZ->x[1], pZ->x[2], pZ->y[0], pZ->y[1]);
}

 int main(int argc, char*argv[])
{
 char *fn = "data.txt";
 FILE *fh = fopen(fn, "rt");
 if (fh != NULL) {
    char buffer[1024];     /* maximal 1024 Zeichen pro Zeile */
    struct Zahl_v01 Z_v01; /* unser Zahlenproblem als Struktur */
    while (fgets(buffer, sizeof(buffer), fh) != NULL) {
       if (getnumbers_v01(buffer, &Z_v01, sfmt_v01) == 5) {
          printnumbers_v01(&Z_v01, pfmt_v01); puts("");
       }
    }
    fclose(fh);
 }

 return 0;
}
Das ist natürlich nur ein (sogar funktionierender) Rahmen. Du kannst das beliebig erweitern und abändern.
 
Zurück
Oben