Vergleichen von Zeilen einer Textdatei

dodo217

Grünschnabel
Guten Abend Freunde,
Und zwar möchte ich die Zeilen einer Textdatei mit einer vorgegebenen Lösung vergleichen.
Die Textdatei ist folgendermaßen aufgebaut:
Code:
DDCBEEEABEDEBABBECBB
7592 DDCBEEEABEDEBABBCCBB
1911 DDCDEEEABEDEBAXBECBC
2777 DDCBEEEABEDEBABBECBB
9546 DDCBEEEADEDEBEBEECBB
1982 DBCBEEAABEDEBABBECBB
....

Es geht um einen multiple choice Test. Die erste Zeile ist die Lösung des Testes, und die darauffolgenden Zeilen beinhalten die Nummern der jeweiligen Kandidaten und ihre Lösungen.
Nun möchte ich praktisch die angegebenen Antworten mit der Lösung vergleichen(Richtig=1 Punkte, Falsch=-1, X bedeutet keine Antwort=0 Punkte).

Hier mein Code soweit:
C:
#include<stdio.h>
#include <string.h>
#include<stdlib.h>

int main()
{
    int punkteK;
    int n=40;
    int i,x;
    char Antworten[4096];
    char schluessel[n];
    FILE *datei_A;
    char *datei = "exam.dat" ;
    datei_A = fopen(datei,"r");

    if(datei_A != NULL)
    {
        printf("Datei %s wurde geoeffnet !\n",datei);
        fgets(schluessel,n,datei_A);
        printf("Der Schluessel ist: %s",schluessel);

for(i=2;i<=300;i++)

{
    if(fgets(Antworten,n,datei_A))
}
        for(x=1;x<=25;x++)
    {


    {
       if(Antworten[x+5]==schluessel[x])
       {
           punkteK=punkteK +1;
       }
       else if(Antworten[x+5]=='X')
       {
           punkteK=punkteK +0;
       }
           else
           {
               punkteK=punkteK -1;
           }
       }
    
    }
    printf("%i",punkteK);

        fclose(datei_A);
    }
    else{
        printf("\nDatei %s existiert nicht!\n",datei);
    }
    return 0;
}

Fehler werden zwar nicht angezeigt, aber ich bin mir unsicher ob er die zwei richtigen Antworten miteinander vergleicht.
Falls jemand einen Lösungsansatz meines Problems hätte, wäre ich äußerst dankbar.
LG

Edit: Kleine Änderungen vorgenommen. Als Test habe ich nur die erste Zeile der Antworten mit den Lösungen vergleichen lassen. Als Punktewert bekomme ich 15, was aber nicht stimmen kann.
 
Zuletzt bearbeitet:

sheel

I love Asm
Hi

ein paar Gegenfragen zuerst:

Soll es in C sein, oder wäre C++ auch ok?

Warum bekommt man bei Richtig 4 Punkte?
 

dodo217

Grünschnabel
Es soll in C sein, und habs auf 1 Punkt geändert.
Mit der ersten Zeile vergleicht ers nun richtig. Es kommt als Punktzahl 18 raus.
Bei mehreren Antworten kommt aber definitiv eine falsche Zahl raus.
Kann sein, dass hier eine Schleife zum Zeilensprung fehlt?
 

cwriter

Erfahrenes Mitglied
Hallo dodo217

Hier mein Code soweit:
Wunderbar. Eine Frage, wie sie im Bilderbuch steht :)
Ich sehe ein paar Probleme:
C++:
int punkteK;
//....
punkteK=punkteK +4;

Du verwendest eine uninitialisierte Variable, d.h. punkteK kann irgendwas sein. Und irgendwas + 4 ist immer noch irgendwas. Die Lösung hier ist recht simpel:
C++:
int punkteK = 0; //Deklarieren und initialisieren
Damit kannst du sicher sein, dass punkteK immer bei 0 beginnt.

C++:
for(i = 2; i <= 300; i++)
Warum genau diese Werte? Das Tolle an fgets() ist ja, dass es fertig ist, wenn es fertig ist. Du musst keine künstliche Begrenzung auf 298299 einbauen. (Kannst du natürlich, wenn das entsprechend verlangt ist).

C++:
if(fgets(Antworten,n,datei_A))
kannst du entsprechend als while()-Bedingung haben. Allerdings scheiden sich hier ein bisschen die Geister:
fgets() gibt einen char* zurück. Wenn nix mehr zu lesen war, gibt fgets() NULL (unter C++11: nullptr) zurück. Das ist zwar auch 0, allerdings kein (int)0, sondern ein (void*)0. Manche Programmierer bevorzugen daher die explizite Bedingung
C++:
if(fgets(Antworten,n,datei_A) == NULL)
Für die meisten Anwendungsfälle geht aber auch die implizite Umwandlung - der Standard garantiert sie einfach nicht.
C++:
for(x=1;x<=25;x++)
Du überspringst damit die erste Antwort!
Denn in
C++:
if(Antworten[x+5]==schluessel[x])

Wird das zu Antworten[6] im ersten Durchlauf, und da Strings bei 0 beginnen, gilt:
Code:
0123456...
7592 DDCBEEEABEDEBABBCCBB
Somit ist '7' an Antworten[0], '5' an Antworten[1] und so weiter.

C++:
printf("%i",punkteK);
fclose(datei_A);
Du schliesst die Datei direkt nach dem 1. Durchlauf (der 2. Zeile der Datei). Danach ist das Verhalten von fgets() eigentlich undefiniert - aber du scheinst Glück (oder Pech) gehabt zu haben. Wenn du das fclose() direkt vor return 0; setzt, sollte es gehen.

Noch was zu deinem Ansatz, die Werte zu lesen: Du hoffst darauf, dass die Zahlen immer 4 Stellen lang sind und immer nur 1 Abstand dazwischen steht. Das geht, wenn es entsprechend definiert ist und ist an sich völlig korrekt.
Falls du aber variable Zahlen- und Antwortstringlängen haben willst, solltest du dir scanf() ansehen. Sehr praktisch, sehr effizient, sehr allgemeingültig.

Gruss
cwriter

/EDIT: Entweder habe ich das vorhin übersehen oder deine Änderungen haben neue Fehler produziert - irgendwie...
Könntest du bitte deinen Code, so wie er im Editor steht, wenn möglich schön eingerückt, nochmals neu hier posten? Denn so, wie er jetzt da steht, nimmt den kein Compiler an.
 
Zuletzt bearbeitet:

dodo217

Grünschnabel
Erstmal vielen Dank für den hilfreichen Beitrag.
Habe meinen Code soweit möglich abgeändert.
C:
#include<stdio.h>
#include <string.h>
#include<stdlib.h>

int main()
{
    int punkteK=0;
    int n=40;
    int i,x;
    char Antworten[4096];
    char schluessel[n];
    FILE *datei_A;
    char *datei = "exam.dat" ;
    datei_A = fopen(datei,"r");

    if(datei_A != NULL)
    {
        printf("Datei %s wurde geoeffnet !\n",datei);
        fgets(schluessel,n,datei_A);
        printf("Der Schluessel ist: %s",schluessel);


for(i=2;i<=80;i++)

{
    if(fgets(Antworten,n,datei_A))
        printf("%s",Antworten);

}



        for(x=0;x<=20;x++)



    {
       if(Antworten[x+5]==schluessel[x])
       {
           punkteK=punkteK +1;
       }
              else if(Antworten[x+5]=='X')
       {
           punkteK=punkteK +0;
       }
           else
           {
               punkteK=punkteK -1;
           }


    }

    printf("%i",punkteK);


    }




    else{
        printf("\nDatei %s existiert nicht!\n",datei);
    }

    fclose(datei_A);
    return 0;
}

Die richtige Punktzahl kam nur raus, wenn ich nach fgets das zeilenweise Einlesen gleich wieder geschlossen habe.
C:
for(i=2;i<=80;i++)

{
    if(fgets(Antworten,n,datei_A))
        printf("%s",Antworten);

}

Nun bekomme ich bei mehreren Antworten die komplett falsche Punktzahl heraus.
Kann das daran liegen, dass die Schleife gleich geschlossen wird und er nicht Zeile für Zeile vergleicht?
 

cwriter

Erfahrenes Mitglied
Hallo dodo217


Zuerst ein bisschen OT:
@sheel: Bin ich der einzige, der den Code irgendwie nicht kopieren kann?

also, aufgrund technischer Schwierigkeiten: In Zeile 23 hast du eigentlich alles initialisiert und bist bereit, einzulesen. Dann gehst du in den Loop, der immer 79x ausgeführt wird. Warum?

In diesem Loop liest du 40 Zeichen in (Antworten) ein. Und das dann 79x. Warum?

Ich denke, es besteht entweder ein Klammerfehler oder ein Missverständnis:
fgets() liest IMMER nach (Antworten + 0) ein. D.h. die erste Zeile kommt, passt LOCKER in den Array hinein, denn du liest immer nur 40 Zeichen, hast aber 4096 Zeichen Platz. Warum? Die Zeichenketten werden nicht aneinandergehängt, sondern überschreiben sich immer wieder.

Dann hast du also die letzte gültige Zeile in Antworten gespeichert.

Dann gehst du bis Antworten[25]. Kannst du machen, allerdings gibst du so immer einen Punkt zu wenig: Strings enden korrekterweise mit '\0' (Zero-Terminator). Und dein String ist 25 Zeichen + 1x '\0' -> '\0' ist an Antwort[25].
Dein Schlüssel endet hingegen mit '\n' in der Datei, d.h. schluessel[20] ist '\n'.
Das Problem kannst du durch ein < statt einem <= im for-loop beheben, aber alternativ kannst du auch einfach beim ersten non-alpha-num-char abbrechen (siehe isalnum()).

Bei diesem kurzen Code kann ich dir folgendes empfehlen:
Beginne nochmals, und überlege dir genau, was du eigentlich brauchst.
Eine kleine Liste:
1. Du musst den Schlüssel (die erste Zeile) einlesen und speichern.
2. Du musst Zeilenweise den Schlüssel mit der Antwort in dieser Zeile vergleichen.

Da bietet sich doch folgende Struktur an:

C++:
//Init code, Datei öffnen, erste Zeile lesen (wenn diese nicht existiert, direkt abbrechen) etc...

while(fgets(antwort, maxanslength, file) != NULL)
{
    //Zeile lesen, Antwort mit schlüssel vergleichen, Punkte aufaddieren, dann ausgeben.
}

//Aufräumen (Datei schliessen)

//FERTIG!

Dein Versuch, die Datei als ganzes einlesen, mag zwar performancemässig eine nette Idee sein, aber dann solltest du auch nicht fgets() verwenden.

Gruss
cwriter

/EDIT: Falls du ein bisschen eine Orientierung brauchst: Die Aufgabe lässt sich mit 2 ineinandergestapelten Schleifen lösen.
 

sheel

I love Asm
Nach cwriters tollen Beiträgen hab ich selber nicht mehr viel zum dazusagen :)

Nur etwas, das in dem Fall auch nicht so dringend ist:
Code:
char *datei = "exam.dat" ;
Wenn das so geschrieben wird darf der Inhalt vom String nicht verändert werden. Ok, machst du ja auch nicht. Aber wenn man es doch macht kommt nicht unbedingt ein Compilerfehler (oft nur eine Warnung und manchmal gar nichts).

C-Compiler verlassen sich bei einigen Sachen drauf, dass man alles richtig macht, und prüfen es nicht (bzw. teilweise können solche Sachen gar nicht vollständig überprüft werden (Turings Halteproblem usw.)). Stattdessen kann beim Ausführen irgendwas unerwartetes passieren (buchstäblich als "undefiniertes Verhalten" bekannt). Manchmal kann alles gut gehen, manchmal hat man Abstürze vom Programm, und manchmal Sachen wie zB. Variablenwerte die sich grundlos irgendwann ändern, Codezeilen die übersprungen werden usw. In dem Fall ist die Fehlersuche ziemlich mühsam. Direkt erkennen, dass die Variable datei schuld ist, kann man nicht.

Für diesen Fall (also Pointer zu fixem unveränderbarem String) kann man einfache Schlampigkeitsfehler aber vermeiden:
Code:
const char * const datei = "exam.dat" ;
Direkte Veränderungen von datei (Adressen und/oder Buchstaben) führen damit zu Compilerfehlern. Mit etwas Pointer-"Akrobatik" könnte man den Compiler zwar noch immer reinlegen, aber zumindest sollte man so versehentliche Änderungen im Code finden.

...

Bin ich der einzige, der den Code irgendwie nicht kopieren kann?
Hm. Bei mir gehts. Browser+OS?