2016-11-29 2 views
4

Ich habe mit diesem Problem für 2 Tage gesessen und ich kann nicht herausfinden, was ich falsch mache. Ich habe versucht zu debuggen (irgendwie? Noch irgendwie neu), folgte diesem Link: https://ericlippert.com/2014/03/05/how-to-debug-small-programs/ Und ich habe Google und alle möglichen Dinge ausprobiert. Grundsätzlich ich aus einer Datei mit diesem Format gerade lese:Wird nicht von Datei zu Struktur lesen

R1 Fre 17/07/2015 18.00 FCN - SDR 0 bis 2 3,211

und ich muss das Programm machen lesen Sie in einer Struktur, aber wenn Ich versuche, die Informationen, die falsch ausgegeben werden, auszudrucken. Mein Code sieht wie folgt aus:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#define MAX_INPUT 198 

typedef struct game{ 
    char weekday[4], 
      home_team[4], 
      away_team[4]; 
    int round, 
      hour, 
      minute, 
      day, 
      month, 
      year, 
      home_goals, 
      away_goals, 
      spectators;}game; 

game make_game(FILE *superliga); 

int main(void){ 
    int input_number, 
      number_of_games = 198, 
      i = 0; 
    game tied[MAX_INPUT]; 

    FILE *superliga; 
    superliga = fopen("superliga-2015-2016.txt", "r"); 

    for(i = 0; i < number_of_games; ++i){ 
       tied[i] = make_game(superliga); 
       printf("R%d %s %d/%d/%d %d.%d %s - %s %d - %d %d\n", 
         tied[i].round, tied[i].weekday, tied[i].day, tied[i].month, 
         tied[i].year, tied[i].hour, tied[i].minute, tied[i].home_team, 
         tied[i].away_team, tied[i].home_goals, tied[i].away_goals, 
         tied[i].spectators);} 

fclose(superliga); 

return 0; 
} 

game make_game(FILE *superliga){ 
    double spect; 
    struct game game_info; 

    fscanf(superliga, "R%d %s %d/%d/%d %d.%d %s - %s %d - %d %lf\n", 
      &game_info.round, game_info.weekday, &game_info.day, &game_info.month, 
      &game_info.year, &game_info.hour, &game_info.minute, game_info.home_team, 
      game_info.away_team, &game_info.home_goals, &game_info.away_goals, 
      &spect); 

     game_info.spectators = spect * 1000; 

    return game_info; 
} 
+1

Ich denke, die '\ n ' –

+1

Verwenden Sie ein Problem in der' fscanf' dazu führen könnten, ein Debugger, um den Code Zeile für Zeile durchzugehen. Und überprüfen Sie, was ['fscanf'] (http://en.cppreference.com/w/c/io/fscanf) zurückgibt. –

+0

Textdatei Datenanalyse in C saugt. Ich fühle, dass du das tun musst, wenn es hilft. Wissen Sie, dass andere Sprachen Ihnen viel bessere Werkzeuge dafür geben, wenn Sie dort ankommen. –

Antwort

0

Wenn jede Zeile in der Datei ein separater Datensatz ist, Sie jede Zeile als String lesen sollte, dann versuchen, jede Zeichenfolge zu analysieren.

(Beachten Sie, dass dies auch die zusätzliche Funktion von spekulativen Parsing hat. Sie können versuchen, die Linie in verschiedenen Formaten Parsen, und die man akzeptieren, die korrekt analysiert Ich mag diese verwenden, wenn ich zB Vektoreingaben akzeptieren, so dass der Benutzer x y z verwenden können, x, y, z, x/y/z, (x,y,z), [x,y,z], <x y z>, <x,y,z>, und so weiter, je nachdem, was sie wollen. Es ist nur ein zusätzliches scanf pro Format, nachdem alle.)

Zeilen zu lesen, Sie können fgets() in einen lokalen Puffer verwenden. Der lokale Puffer muss lang genug sein. Wenn das Programm nur auf POSIX.1-Computern ausgeführt werden soll (d. H. Nicht unter Windows), können Sie stattdessen getline() verwenden, wodurch der angegebene Puffer bei Bedarf dynamisch neu zugewiesen werden kann, sodass Sie nicht auf eine bestimmte Zeilenlänge beschränkt sind.

Um die Zeichenfolge zu analysieren, verwenden Sie sscanf().

Beachten Sie, dass alle Tabs, Leerzeichen und Zeilenumbrüche im Muster in allen scanf-Funktionen genau gleich behandelt werden: Sie geben eine beliebige Anzahl von Leerzeichen an. Mit anderen Worten bedeutet \n nicht "und dann eine neue Zeile"; es bedeutet dasselbe wie ein Leerzeichen, d. h. "und möglicherweise einige Leerzeichen hier". Alle Konvertierungen außer %c und %[ überspringen jedoch automatisch alle führenden Leerzeichen. Mit Ausnahme eines Leerzeichens vor einem dieser beiden sind die Leerzeichen im Muster nur für uns Menschen von Bedeutung, sie haben keinen funktionalen Effekt beim Scannen.

Alle scanf-Funktionsfamilien geben die Anzahl der erfolgreichen Konvertierungen zurück. (Die einzige Ausnahme ist die "Konvertierung" %n, die die Anzahl der verbrauchten Zeichen ergibt; einige Implementierungen enthalten sie in der Umsetzungszählung und einige andere nicht.) Wenn das Ende der Eingabe vor der ersten Konvertierung oder ein Lesefehler auftritt auftritt oder die Eingabe nicht mit dem festen Teil des Musters übereinstimmt, geben die Funktionen EOF zurück.

Auch wenn Sie das Speichern des Ergebnisses einer Konvertierung unterdrücken - wenn Sie beispielsweise ein Wort in der Eingabe haben, das Sie nicht benötigen, können Sie es konvertieren, aber mit %*s verwerfen - es wird gezählt. So gibt zum Beispiel sscanf(line, " %*d %*s %*d") 3 zurück, wenn die Zeile mit einer Ganzzahl beginnt, gefolgt von einem Wort (alles, was keine neue Zeile ist oder Whitespace enthält), gefolgt von einer Ganzzahl.

Anstatt die Funktion die geparste Struktur zurückzugeben, übergeben Sie einen Zeiger auf die Struktur (und das Dateihandle, von dem gelesen werden soll), und geben Sie einen Statuscode zurück. Ich bevorzuge 0 für Erfolg und nicht Null für Fehler, aber fühlen Sie sich frei, das zu ändern.

Mit anderen Worten, würde ich vorschlagen, dass Sie Ihre Lesefunktion in

ändern
#ifndef GAME_LINE_MAX 
#define GAME_LINE_MAX 1022 
#endif 

int read_game(game *one, FILE *in) 
{ 
    char buffer[GAME_LINE_MAX + 2]; /* + '\n' + '\0' */ 
    char *line; 

    /* Sanity check: no NULL pointers accepted! */ 
    if (!one || !in) 
     return -1; 

    /* Paranoid check: Fail if read error has already occurred. */ 
    if (ferror(in)) 
     return -1; 

    /* Read the line */ 
    line = fgets(buffer, sizeof buffer, in); 
    if (!line) 
     return -1; 

    /* Parse the game; pattern from OP's example: */ 
    if (sscanf(line, "R%d %3s %d/%d/%d %d.%d %3s - %3s %d - %d %d\n", 
        &(one->round), one->weekday, 
        &(one->day), &(one->month), &(one->year), 
        &(one->hour), &(one->minute) 
        one->home_team, 
        one->away_team, 
        &(one->home_goals), 
        &(one->away_goals), 
        &(one->spectators)) < 12) 
     return -1; /* Line not formatted like above */ 

    /* Spectators in the file are in units of 1000; convert: */ 
    one->spectators *= 1000; 

    /* Success. */ 
    return 0; 
} 

Um die obige Funktion in einer Schleife zu verwenden, Spielen nacheinander von der Standardeingabe lesen (stdin):

game g; 

while (!read_game(&g, stdin)) { 

    /* Do something with current game stats, g */ 

} 

if (ferror(stdin)) { 

    /* Read error occurred! */ 

} else 
if (!feof(stdin)) { 

    /* Not all data was read/parsed! */ 

} 

Die zwei if Klauseln oben sind, um zu überprüfen, ob es einen echten Lesefehler gab (wie in, ein Problem mit der Hardware oder etwas Ähnlichem), und ob es ungelesene/ungeparste Daten gab (nicht am Ende der Datei) .

Im Vergleich zum OP gibt es zwei Unterschiede im Scanmuster: Zunächst sind alle analysierten Strings auf 3 Zeichen begrenzt, da die Struktur nur Platz für jeweils 3 + 1 hat. Das eine Zeichen ist für das Ende der Zeichenfolge '\0' reserviert, das nicht in der maximalen Länge für %s gezählt wird. Zweitens analysiere ich die Anzahl der Zuschauer direkt und multipliziere das Feld einfach mit 1000 wenn es erfolgreich ist.

Beachten Sie auch, wie ich one->weekday, one->home_team und one->away_team verwendet, um auf die Zeichenarrays zu verweisen. Dies funktioniert, weil eine Array-Variable so verwendet werden kann, als wäre sie ein Zeiger auf das erste Element in diesem Array. (Gegeben char a[5];, a und &a und &(a[0]) können alle verwendet werden, um sich auf das erste Element in dem Array a zu beziehen). Ich verwende diese "rohe Form" gerne beim Scannen, weil es einfacher ist, sie an %s Konvertierungen anzupassen und sicherzustellen, dass das Muster mit den Parametern übereinstimmt.

+0

Vielen Dank für Ihre Antwort! Ich änderte es und änderte '* eins' auf '* g', wegen einiger Fehler. Leider bekomme ich jetzt einen Fehler, mit dem ich nicht umgehen kann: error: inkompatible Typen beim Zuweisen von 'game {aka struct game}' vom Typ 'int': ticked [i] = read_game (g, im); – NikolajAB

+0

@NikolajAB: Danke; Ich habe das Funktionsbeispiel korrigiert. Ich habe einen oder zwei Absätze vor dem Code-Snippet als Erklärung sowie ein Anwendungsbeispiel hinzugefügt. Im Grunde, anstatt eine 'Spiel'-Struktur zurückzugeben, müssen Sie einen Zeiger darauf übergeben; Die Funktion gibt '0' zurück, wenn Erfolg, und ungleich Null, wenn sie fehlschlägt. Mit anderen Worten, benutze 'if (read_game (& (gebunden [i]), in)) {/ * Spiel konnte nicht gelesen werden! * /} else {/ * Spiel wurde erfolgreich gelesen * /} '. –

0

Das Problem liegt in Ihrer Datei. Es beginnt mit Leerzeichen, nicht mit R, wie Sie in der Kontrollzeichenfolge angegeben haben.

Überprüfen Sie den Rückgabewert von fscanf() und Sie werden sehen, dass es jedes Mal Null ist.

Wenn Sie eine führende Leerzeichen zu Ihrer fscanf() -Aufruf hinzufügen, wird Ihr Problem gelöst werden, wie folgt aus:

fscanf(superliga, " R%d %s %d/%d/%d %d.%d %s - %s %d - %d %lf\n", 
      &game_info.round, game_info.weekday, &game_info.day, &game_info.month, 
      &game_info.year, &game_info.hour, &game_info.minute, game_info.home_team, 
      game_info.away_team, &game_info.home_goals, &game_info.away_goals, 
      &spect); 
+0

Ich habe versucht, ein Leerzeichen hinzuzufügen und ich bekomme die gleiche seltsame Ausgabe. – NikolajAB

+0

Sind Sie sicher? Hier funktioniert Ihr Programm nach dieser einen Änderung perfekt. – yLaguardia

+0

Verdammt, habe gerade versucht, meinen Code von hier und in eine neue Datei zu kopieren, anstatt meinen alten zu bearbeiten, und es hat funktioniert! Ich danke dir sehr! Wie frustrierend, dass so ein kleines Ding so viel Kummer verursachen kann! – NikolajAB