Benötige Hilfe in C Programmierung ppm Ausgabe

vabu84

Grünschnabel
Hallo Leute, ich muss eine Notenverwaltungssoftware in der C Programmierung schreiben und benötige Hilfe.
Aufgabenstellung:
Es soll dem Anwender möglich sein eine Prüfung zu erfassen, die als Dateiname abgelegt wird.Nachdem dies vorgenommen wurde kann die Lehrkraft zu einem beliebigen Zeitpunkt, eine beliebige Anzahl Noten in diese Prüfung eintragen. Nachdem mehr als 2 Prüfungen eingetragen wurden, hat die Lehrkraft die möglichkeit ein Diagramm im Dateiformat ppm generieren
zu lassen. Um dies bewerkstelligen zu können müssen Sie das einfache Ascii BildFormat studieren.
https://de.wikipedia.org/wiki/Portable_Anymap ,heisst es in der Aufgabenstellung.
irgend so ein Diagramm ausgeben für die Noten.
z.B.
000000
111100 Note 4
000000
111110 Note 5 ... etc DIE Aufgabenbeschreibung befindet sich im Bild Anhang

Mein Programmcode
C:
#include <stdlib.h>
#include <stdio.h>

struct {
   char vorname[20];
   char nachname[30];
   char note1[80];
   char note2[80];
   char note3[80];

} adressen;

void speichern(void) {
   FILE *save = fopen("adressen.dat","r+b");
   if( NULL == save ) {
      save = fopen("adressen.dat","w+b");
      if( NULL == save ) {
         fprintf(stderr,"Kann \"adressen.dat\" nicht öffnen!\n");
         return;
      }
   }
   /* FILE-Zeiger save auf das Ende der Datei setzen */
   fseek(save, 0, SEEK_END);

   /* Wir schreiben eine Adresse ans Ende von "adressen.dat". */
   if(fwrite(&adressen, sizeof(adressen), 1, save) != 1) {
      fprintf(stderr, "Fehler bei fwrite...!!!\n");
      return;
   }
   /* Wir geben unseren FILE-Zeiger wieder frei. */
   fclose(save);
}

void ausgabe(void) {
   FILE *output = fopen("adressen.dat","r+b");
   if( NULL == output ) {
      fprintf(stderr,"Kann \"adressen.dat\" nicht öffnen!\n");
      return;
   }
   /* Wir lesen alle Adressen aus "adressen.dat". */
   while(fread(&adressen, sizeof(adressen), 1, output) == 1) {
      printf("Vorname Schueler1. %s",adressen.vorname);
      printf("Nachname......... %s",adressen.nachname);
      printf("note1...........   %s",adressen.note1);
      printf("\n");
      printf("Vorname.Schueler2. %s",adressen.vorname);
      printf("Nachname......... %s",adressen.nachname);
      printf("note............. %s",adressen.note2);
      printf("\n\n");
      printf("Vorname.Schueler3. %s",adressen.vorname);
      printf("Nachname......... %s",adressen.nachname);
      printf("note............. %s",adressen.note3);
      printf("\n\n");
      printf("Vorname.......... %s",adressen.vorname);
      printf("Nachname..........%s",adressen.nachname);
      printf("note............. %s",adressen.note1);
      printf("\n\n");
       printf("Vorname..........%s",adressen.vorname);
      printf("Nachname..........%s",adressen.nachname);
      printf("note..............%s",adressen.note2);
      printf("\n\n");
     /*WIE GIBT MAN BILD AUS IN KONSOLE??? */
      /* Wir lesen alle Adressen aus "adressen.dat". */
   }
   fclose(output);
}
void eingabe(void) {
   printf("Vorname Schueler1....");
   fgets(adressen.vorname, sizeof(adressen.vorname), stdin);
   printf("Nachname....");
   fgets(adressen.nachname, sizeof(adressen.nachname),stdin);
   printf("note  1:....");
   fgets(adressen.note1, sizeof(adressen.note1), stdin);
    printf("note 2:....");
   fgets(adressen.note2, sizeof(adressen.note2), stdin);
    printf("note 3:....");
   fgets(adressen.note3, sizeof(adressen.note3), stdin);
   printf("Vorname Schueler2....");
   fgets(adressen.vorname, sizeof(adressen.vorname), stdin);
   printf("Nachname....:");
   fgets(adressen.nachname, sizeof(adressen.nachname),stdin);
   printf("note  1....:");
   fgets(adressen.note1, sizeof(adressen.note1), stdin);
   printf("note 2....:");
   fgets(adressen.note2, sizeof(adressen.note2), stdin);
    printf("note 3:....");
   fgets(adressen.note3, sizeof(adressen.note3), stdin);
   printf("Vorname Schueler3....");
   fgets(adressen.vorname, sizeof(adressen.vorname), stdin);
   printf("Nachname:....");
   fgets(adressen.nachname, sizeof(adressen.nachname),stdin);
    printf("note  1:....");
   fgets(adressen.note1, sizeof(adressen.note1), stdin);
     printf("note 2:....");
   fgets(adressen.note2, sizeof(adressen.note2), stdin);
     printf("note 3:....");
   fgets(adressen.note3, sizeof(adressen.note3), stdin);
   speichern();
}

int main(void) {
   int wahl;

   do {
      printf("Was wollen Sie machen\n");
      printf("-1- Neuen Note erfassen\n");
      printf("-2- Alle Datensaetze ausgeben\n");
      printf("-3- Programm beenden\n\n");
      printf("Ihre Auswahl : ");
      do {
         scanf("%d",&wahl);
      } while(getchar() != '\n');
      switch(wahl) {
         case 1 : eingabe();        break;
         case 2 : ausgabe();        break;
         case 3 : printf("...Programm wird beendet\n");
                  break;
         default: printf(">>%d<< ???\n",wahl);
      }
   } while(wahl != 3);

return;
   }
Vielen Dank für eure Hilfe vanessa
 

Anhänge

  • Aufgabe Programmierungt.PNG
    Aufgabe Programmierungt.PNG
    106,1 KB · Aufrufe: 7
Zuletzt bearbeitet von einem Moderator:

sheel

I love Asm
Hi

a) Bitte formatier deinen Post ordentlich. Einen Hinweis, wie man Codetags macht, hast du bereits bekommen.
b) Was ist das Problem? Wenn du wen suchst, der dir einfach so die Hausübung fertig macht, bist du hier falsch.
 

cwriter

Erfahrenes Mitglied
Hallo vabu84

Wenn du wen suchst, der dir einfach so die Hausübung fertig macht, bist du hier falsch.
Genau so ist es. Ich nehme mal an, dass der Abgabetermin mittlerweile schon verstrichen ist, dennoch hier ein paar Anmerkungen:
C++:
void speichern(void) //Eine Funktion, die fehlschlagen kann, sollte einen Rückgabewert haben.
{
    FILE *save = fopen("adressen.dat", "r+b");
    if (NULL == save) //Für den Mensch ist save == NULL einfacher zu lesen. Grund: Du weisst direkt, welche Variable du überprüfen willst.
    {
        save = fopen("adressen.dat", "w+b");
        if (NULL == save)
        {
            fprintf(stderr, "Kann \"adressen.dat\" nicht öffnen!\n");
            return;
        }
    }
    /* FILE-Zeiger save auf das Ende der Datei setzen */
    fseek(save, 0, SEEK_END);

    /* Wir schreiben eine Adresse ans Ende von "adressen.dat". */
    if (fwrite(&adressen, sizeof(adressen), 1, save) != 1)
    {
        fprintf(stderr, "Fehler bei fwrite...!!!\n");
        return;
    }
    /* Wir geben unseren FILE-Zeiger wieder frei. */
    fclose(save);
}
Du öffnest dieselbe Datei einmal, um zu sehen, ob sie existiert und einmal, um sie zu erstellen, falls sie nicht existiert. Zudem schreibst du nur ans Ende der Datei. Dafür gibt es aber schon den Modus "a+". Dieser hat folgende Eigenschaften:
  • Die Datei wird erstellt, falls sie nicht existiert
  • Schreiboperationen können nur am Ende ausgeführt werden
  • Leseoperationen können überall ausgeführt werden
Umgesetzt heisst das:
C++:
int speichern(void)
{
    FILE *save = fopen("adressen.dat", "a+b");
    if (save == NULL)
    {
        fprintf(stderr, "Kann \"adressen.dat\" nicht öffnen!\n");
        return -1; //Melde den Fehler an den Caller
    }

    /* Wir schreiben eine Adresse ans Ende von "adressen.dat". */
    if (fwrite(&adressen, sizeof(adressen), 1, save) != 1)
    {
        fprintf(stderr, "Fehler bei fwrite...!!!\n");
        return -2;
    }
    /* Wir geben unseren FILE-Zeiger wieder frei. */
    fclose(save);
    return 0;
}
Der Code wirkt schon sehr viel ordentlicher, oder?

C++:
void ausgabe(void)
{
    FILE *output = fopen("adressen.dat", "r+b"); //r+ ist lesen und schreiben. Du schreibst nicht -> öffne als "rb"
    if (NULL == output) //"output"? -> Nomenklatur
    {
        fprintf(stderr, "Kann \"adressen.dat\" nicht öffnen!\n"); //Sonderzeichen wie öäü geben Probleme. Nicht immer, aber oft. Nimm bspw. '\201' statt 'ü': https://www.c-plusplus.net/forum/39326-full
        return;
    }
    /* Wir lesen alle Adressen aus "adressen.dat". */
    while (fread(&adressen, sizeof(adressen), 1, output) == 1)
    {
        /*Hier haben wir das erste grosse Problem: Du loopst über alle Adressen
        (Nomenklatur überdenken, das hat nichts (0.0%) mit Adressen zu tun),
        aber du gibst denselben Schüler als mehrfache Existenz aus (zu Testzwecken?)
        Zudem hast du extreme Redundanzen.
        */
        printf("Vorname Schueler1. %s", adressen.vorname);
        printf("Nachname......... %s", adressen.nachname);
        printf("note1........... %s", adressen.note1);
        printf("\n");
        printf("Vorname.Schueler2. %s", adressen.vorname);
        printf("Nachname......... %s", adressen.nachname);
        printf("note............. %s", adressen.note2);
        printf("\n\n");
        printf("Vorname.Schueler3. %s", adressen.vorname);
        printf("Nachname......... %s", adressen.nachname);
        printf("note............. %s", adressen.note3);
        printf("\n\n");
        printf("Vorname.......... %s", adressen.vorname);
        printf("Nachname..........%s", adressen.nachname);
        printf("note............. %s", adressen.note1);
        printf("\n\n");
        printf("Vorname..........%s", adressen.vorname);
        printf("Nachname..........%s", adressen.nachname);
        printf("note..............%s", adressen.note2);
        printf("\n\n");
        /*WIE GIBT MAN BILD AUS IN KONSOLE??? */
        /*Antwort:
        /* Wir lesen "alle" jede Adresse"n" aus "adressen.dat". */
        //Unterschied: Alle lesen impliziert, dass ALLE Adressen(?) in den Speicher geladen werden. Das ist aber nicht der Fall.
        //Es wird jede "Adresse" einzeln gelesen und du hast keine Chance, danach noch auf die vorhergehende Adresse direkt
        //zuzugreifen.
    }
    fclose(output);
}

Hier gibt es Einiges zu tun.
1. Nomenklatur. Bei diesem kleinen Beispiel kommt das noch nicht so darauf an, aber wenn du mal komplexeren Code vor der hast, dann würde dir diese Nomenklatur nicht helfen. "adressen" beinhaltet nicht eine einzige Adresse, weder im Semantischen (Computerebene), noch im Abstrakten (Deine Gedanken dahinter).
2. Du verwendest 20(!) printf()-Aufrufe, die alle in einen einzigen Aufruf zusammengefasst werden könnten. Zudem macht es rein logisch nicht wirklich Sinn, denselben Namen 5 mal auszugeben. Ich unterstelle dir einfach mal Copy-Pasting, habe ich Recht?
3. *Seufz*. Und jetzt erst habe ich
eine beliebige Anzahl Noten in diese Prüfung eintragen
richtig wahrgenommen. Sorry, aber 3*79 Noten sind nicht "beliebig viele". Die Tipps sind natürlich dennoch gültig, aber bei dieser Aufgabe, es tut mir leid, bleibt für denen Code nur noch Tabula Rasa als gangbare Option.

Ok, dann leite ich dich mal durch (das sollte ja eigentlich dein Lehrer/Professor/Assistent machen, aber doppelt hält besser, nicht wahr?).

Also gut: Die Aufgabenstellung:
Du musst ein Programm erstellen, welches:
Prüfungen verwaltet und Diagramme davon erstellen kann. Dazu braucht es folgende Teile:
a) Diese Prüfungen müssen von dem Benutzer benannt werden und dieser Name soll dann auch als Dateiname gelten.
b) Zu jeder Prüfung soll der Benutzer jederzeit beliebig viele Noten eintragen können.
c) Der Benutzer soll zu jeder Zeit ein Diagramm erstellen können. Dazu musst du das Diagramm nur Codieren bzw. Generieren, aber nicht selbst zeichnen.

Nun zur Analyse: Was brauchst du dafür bzw. was bietet sich an?
Zum Teil a): Du brauchst eine Funktion, die den gewünschten Namen einliest, eventuell eine Dateiendung anhängt und die Datei erstellt.
Dazu sind folgende Funktionen interessant: sprintf, scanf, fopen.

Das Vorgehen dürfte ziemlich straightforward sein.

b) Du musst also eine beliebige Anzahl Zahlen einlesen. Das ist ein bisschen tricky und es gibt keine "beste" Lösung. Entweder du machst einen Buffer, liest dort hinein und schreibst den Buffer ab und zu zurück in die Datei. Anhand dessen, was ich deinem Code entnehmen konnte, ist das ein bisschen zu sophisticated für diese Aufgabe. Also auf die faule Art:
Die Lösung hier ist ziemlich simpel: Loop über scanf("%f") und darin in die Datei "file" schreiben. Als besonderes Syntaxzückerchen (du kannst es aber auch auf deine Art schreiben, das ist egal):
C++:
float mark = 0.f;
while(scanf("%f", &mark) /*"==1" für Typsicherheit*/) fprintf(file, "%f ", mark);
(file öffnen usw. kannst du selbst). Wie funktioniert es? Nun, es braucht eine temporäre Variable, um überhaupt einlesen zu können. scanf() gibt die Anzahl Elemente zurück, die korrekt gelesen wurden. Bei einem %f heisst das: wenn eine Zahl vorliegt, ist der Rückgabewert 1, da das einzige Objekt korrekt gelesen wurde. Ansonsten ist es 0. Nun ist C eine recht typ-unsichere Sprache, d.h. im Speicher selbst ist bool eigentlich ein int-konformer Typ (eine Ganzzahl), und boolean 'false' ist eigentlich nur '0' und boolean true ist alles andere. Also: Wenn das Objekt nicht gelesen werden konnte, wird abgebrochen, ansonsten loopt es weiter. Die Note wird dann direkt wieder in die Datei geschrieben.
Hinweis: Das ist jetzt mit Kommazahlen/-noten. Wenn du nur Ganzzahlen willst, musst du aber nicht viel ändern, um diese zu bekommen.

Nun zur eigentlichen Aufgabe c)
Du musst ein Diagrammformat befolgen, um eine Datei zu erstellen. Nun ist das vorgeschlagene Format eigentlich ein Bildformat und somit ziemlich Blödsinn für diese Aufgabe, aber was soll's.
Du musst eine bestimmte Auflösung und eine gewisse Farbtiefe wählen. Die Auflösung bestimmt, wie gross die Tabelle wird, die Farbtiefe bestimmt, wie fein die Abstufungen sein können. Da PPM vorgeschrieben ist, muss die Farbtiefe 2 sein, die Auflösung ist aber variabel.
Dein "Vorschlag" hat somit die Auflösung 6*x (6 Spalten, x Zeilen) und die Farbtiefe 2 (weiss/schwarz), wobei x die Anzahl eingetragener Noten ist.
Nun die Frage: Schweizer System? Also 1 tiefste Note, 6 beste? Ich nehme dies mal an, aber du kannst es recht einfach umstellen.

Wir können nun die (unsortierten) Noten durchgehen. Also die Prüfungsdatei öffnen und per fscanf auslesen, analog zum einlesen von der Eingabe von b).
C++:
while(...) //Hole nächste Note in "mark"
{
float f= 0.f;
while(f <= 6.f) {
    fprintf(file, "%d ", (f >= mark) ? 0 : 1);
    f += 6.f/float(AuflösungX);
}
fprintf(file, "\n");
}
Wie du sicher feststellen kannst, ist dieser Code kein korrektes C-Programm (aber fast :) ). Zur Syntax: (cond) ? (expr) a : (expr) b heisst folgendes: Falls cond wahr ist, nimm a als Wert, ansonsten nimm b. Du solltest das in if/else umformen können.

So. Mit diesen Hinweisen/Tipps solltest du in der Lage sein, das Programm zu schreiben. Kleiner Tipp: Falls der Abgabetermin tatsächlich schon verstrichen ist: Mach die Aufgabe dennoch nochmals. Es ist immer einfacher danach.

Gruss
cwriter
 
Zuletzt bearbeitet: