Löschen bis "\n" in .txt Datei

#1
Hallo!
Ich arbeite zurzeit an einer To-do Liste. Diese Funktioniert auch soweit einwandfrei, bis auf eine kleinigkeit, die ich einfach nicht behoben bekomme.
Nach eingeben der Aufgaben, ist es möglichAufgaben zu löschen. für alle Aufgaben >1 funktioniert es auch wie gewollt. Sollte ich allerdings die erste Aufgabe löschen, wird der Text gelöscht, allerdings bleibt das \n bestehen, und die Zeile ist damit in der Tabelle leer.

Hat jemand eine Idee woran es liegen könnte?
Und fallen euch sonst noch Dinge auf die Ihr einfacher oder anders gelöst hättet?

C:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


#define MAX 26

struct datum {
    int tag;
    int monat;
    int jahr;
};



struct aufgabe {
    char name[MAX];
    struct datum deadline;
    char ort[MAX];
    };

void clearScanf(void) {
    int intCharacter;
    while ((intCharacter = getchar()) != '\n' && intCharacter != EOF);
}

int checkDate(int tag, int monat, int jahr) {
    //ist es ein korrektes Datum?
    if (monat > 12 || monat < 1) {
        return 0;
    }
    if (monat == 1 || monat == 3 || monat == 5 || monat == 7 || monat == 8 || monat == 10 || monat == 12) {
        if (tag < 1 || tag>31) {
            return 0;
        }
    }
    else if (monat == 4 || monat == 5 || monat == 9 || monat == 11)
    {
        if (tag < 1 || tag>30) {
            return 0;
        }
    }
    else if (monat == 2) {
        // im Februar könnte ein Schaltjahr sein
        if (jahr % 4 == 0 && jahr % 100 != 0) {
            // durch 4 teilbar aber nicht durch 100 --> Schaltjahr
            if (tag < 1 || tag>29) {
                return 0;
            }
        }
        else if (jahr % 100 == 0 && jahr % 400 != 0) {
            // durch 100 teilbar aber nicht durch 400 --> kein Schaltjahr
            if (tag < 1 || tag>28) {
                return 0;
            }
        }
        else if (jahr % 400 == 0) {
            // durch 400 teilbar --> Schaltjahr
            if (tag < 1 || tag>28) {
                return 0;
            }
        }
    }
    //liegt das Datum auch in der Zukunft?
    time_t t = time(NULL);
    struct tm aktuellesDatum = *localtime(&t);
    if (aktuellesDatum.tm_year + 1900 > jahr) {
        return 0;
    }
    else if (aktuellesDatum.tm_year + 1900 == jahr) {
        if (aktuellesDatum.tm_mon + 1> monat) {
            return 0;
        }
        else if (aktuellesDatum.tm_mon + 1 == monat) {
            if (aktuellesDatum.tm_mday >= tag) {
                return 0;
            }
        }
    }
    return 1;
}

struct aufgabe eingabe(void) {

    struct aufgabe Aufgabe;

    char name[MAX + 10];
    struct datum deadline = { -1,-1,-1 };
    char ort[MAX + 10];
    


    printf("Bitte geben Sie den Name der Aufgabe ein: \t");
    scanf("%s", &name);
    while (strlen(name) >= MAX) {
        clearScanf();
        printf("Der Aufgabenname ist zu lang. Bitte geben Sie maximal %d Zeichen ein:\t", MAX - 1);
        scanf("%s", &name);
    }
    strcpy(Aufgabe.name, name);
    clearScanf();

    printf("Bitte geben Sie Datum/Deadline in Format(dd.mm.yyyy) ein: \t");
    scanf("%d.%d.%d", &deadline.tag, &deadline.monat, &deadline.jahr);
    while (checkDate(deadline.tag, deadline.monat, deadline.jahr) != 1) {
        clearScanf();
        printf("Bitte geben Sie ein Datum im Format dd.mm.yyyy an, das in der Zukunft liegt:\t");
        scanf("%d.%d.%d", &deadline.tag, &deadline.monat, &deadline.jahr);
    }
    Aufgabe.deadline = deadline;

    printf("Bitte geben Sie den Ort der Aufgabe ein: \t");
    scanf("%s", &ort);
    while (strlen(ort) >= MAX) {
        clearScanf();
        printf("Der Ort ist zu lang. Bitte geben Sie maximal %d Zeichen ein:\t", MAX - 1);
        scanf("%s", &ort);
    }
    strcpy(Aufgabe.ort, ort);
    clearScanf();


    return Aufgabe;
}

int dateiCheck(void) {
    FILE *fp;
    fp = fopen("toDoList.txt", "r");
    if (fp == NULL) {
        //existiert nicht
        return 0;
    }
    else {
        //existiert
        fclose(fp);
    }
    return 1;
}

int dateiSchreiben(struct aufgabe Aufgabe) {
    int aufgabenNummer = 1;
    //Nummer der Aufgabe herausfinden
    if (dateiCheck() == 1) {
        //letzte Aufgabennummer herausfinden:
        aufgabenNummer = aufgabenanzahl() + 1;
    }
    FILE *fp;
    fp = fopen("toDoList.txt", "a+");
    fprintf(fp, " (%3d) | %25s | %2d.%2d.%4d | %25s |\n", aufgabenNummer, Aufgabe.name, Aufgabe.deadline.tag, Aufgabe.deadline.monat, Aufgabe.deadline.jahr, Aufgabe.ort);
    fclose(fp);
    return 1;
}

void listeAusgeben(void) {

    FILE *fp;
    fp = fopen("toDoList.txt", "r");
    if (fp == NULL) { // sicherheitscheck
        printf("Fehler beim ausgeben der existierenden Liste\n");
    }
    else {
        printf("Ihre aktuelle To-Do-Liste:\n\n\n");
        printf("  Nr.  | %25s |  Deadline  | %25s |\n", "Name der Aufgabe", "Ort");
        printf("-------|---------------------------|------------|---------------------------|\n");

        char p;
        int count = 0;
        do
        {
            p = fgetc(fp);
            if (p != EOF) {
                printf("%c", p);
            }
            if (p == 10) { // immer wenn p = \n ist eine Trennzeile ausgeben
                printf("-------|---------------------------|------------|---------------------------|\n");
            }
        } while (p != EOF);
        printf("\n\n");
        fclose(fp);
    }
}

void startOutput(void) {
    if (dateiCheck() == 1) {
        //vorhandene Liste ausgeben:
        listeAusgeben();
        printf("Bitte w%chlen Sie eine Option aus:\n", 132);
    }
    else {
        printf("Es gibt noch keine Liste. Bitte geben Sie ihre erste Aufgabe ein:\n");
        struct aufgabe Aufgabe = eingabe();
        dateiSchreiben(Aufgabe);
    }

}

int optionenWahl(void) {
    int auswahl = 0;
    printf("(1) - Neue Aufgabe hinzuf%cgen\n", 129);
    printf("(2) - Aufgabe l%cschen\n", 148);
    printf("(3) - Programm beenden\n");
    scanf("%d", &auswahl);
    while (auswahl <1 || auswahl >3) {
        clearScanf();
        printf("Bitte machen Sie eine g%cltige Eingabe.", 129);
        scanf("%d", &auswahl);
    }
    return auswahl;
}

void deleteAufgabe(void) {
    int aufgabe;
    char bestaetigung;
    listeAusgeben();
    printf("Welche Aufgabe soll gel%cscht werden?\n", 148);
    scanf("%d", &aufgabe);
    clearScanf();
    while (aufgabe < 0 || aufgabe > aufgabenanzahl()) {
        printf("Bitte w%chlen Sie eine der existierenden Aufgaben aus\n", 132);
        scanf("%d", &aufgabe);
        clearScanf();
    }
    printf("Aufgabe %d l%cschen?(y|n)\n", aufgabe, 148);
    scanf("%c", &bestaetigung);
    while (bestaetigung != 'y' && bestaetigung != 'n')
    {
        clearScanf();
        printf("Bitte geben Sie entweder y(Ja) oder n(Nein) ein\n");
        scanf("%c", &bestaetigung);
    }
    if (bestaetigung == 'y') {
        int newlinezahler = 0;
        int zahler = 1;
        char c;
        FILE *fp;
        fp = fopen("toDoList2.txt", "w+"); // zweite Datei öffnen, aufgaben aus der erste in die zweite kopieren bis auf die zu löschende
        FILE *fpalt;
        fpalt = fopen("toDoList.txt", "r");
        if (aufgabenanzahl() != 1) {    // falls es mehr als eine Aufgabe gibt, sonst beide Dateien einfach löschen siehe else
            while ((c = fgetc(fpalt)) != EOF)
            {
                if (c == '\n') { //\n zählen
                    newlinezahler++;
                }
                if (newlinezahler == aufgabe - 1) { // wenn die \n einer weniger sind als die zu löschende Aufgabe sind wir vor der zu löschenden Aufgabe
                    while ((c = fgetc(fpalt)) != '\n') { // die Zeichen bis zum nächsten \n werden nicht in die zweite Datei kopiert
                    }
                    newlinezahler++;
                }
                fputc(c, fp);
                if (c == '\n' && newlinezahler <= aufgabenanzahl() - 1) { // Die Zahlen am Anfang einer Zeile müssen angepasst werden
                    zahler++;                                                //Der Zähler ist die neue Aufgabennummer
                    for (int i = 0; i <= 4; i++)                            // Die 5 Zeichen am Anfang einer Zeile fallen lassen
                    {
                        c = fgetc(fpalt);
                    }
                    fprintf(fp, " (%3d", zahler);                            //Stattdessen den Zähler eintragen
                }
            }
            _fcloseall();                                                    // Alle Dateien schließen
            if (remove("toDoList.txt") != 0) {                                //Orginaldatei löschen
                printf("Fehler beim l%cschen", 148);
            }
            if (rename("toDoList2.txt", "toDoList.txt") != 0) {                //neue Datei in alte umbenennen
                printf("Fehler beim umbenennen");
            }
        }
        else
        {                        // Falls nur noch eine Aufgabe existiert beide Dateien löschen
            _fcloseall();
            if (remove("toDoList.txt") != 0) {
                printf("Fehler beim l%cschen", 148);
            }
            if (remove("toDoList2.txt") != 0) {
                printf("Fehler beim l%cschen", 148);
            }
        }
    }
}

int aufgabenanzahl(void) {
    char c;
    int zeilen = 0;
    FILE *fp;
    fp = fopen("toDoList.txt", "r");
    if (fp == NULL)
    {
        return 0;
    }
    else {
        while (!feof(fp))
        {
            c = fgetc(fp);        // Jede Zeile steht für eine neue Aufgabe allso reicht es die Zeilen zu zählen
            if (c == '\n') {
                zeilen++;
            }
        }
        fclose(fp);
        return zeilen;
    }
}


int main(void) {
    int auswahl;
    do
    {
        startOutput();
        auswahl = optionenWahl();
        if (auswahl == 1) {
            system("cls"); // funktioniert nur unter windows
            struct aufgabe Aufgabe = eingabe();
            dateiSchreiben(Aufgabe);
            system("cls");
        }
        else if (auswahl == 2) {
            system("cls");
            deleteAufgabe();
            system("cls");
        }
        else if (auswahl == 3) {
            printf("Auf Wiedersehen.\n");
            exit(0);
        }

    } while (auswahl != 3);
    return 0;
}

Grüße
 

German

Well-Known Member
c-b Experte
#2
Vermutlich (ungetestet)
C:
                if (newlinezahler == aufgabe - 1) { // wenn die \n einer weniger sind als die zu löschende Aufgabe sind wir vor der zu löschenden Aufgabe
                    while ((c = fgetc(fpalt)) != '\n') { // die Zeichen bis zum nächsten \n werden nicht in die zweite Datei kopiert
                    }
                    newlinezahler++;
                }
                else
                    fputc(c, fp);
 
#3
Löst das Problem, löscht dann allerdings bei allen anderen Reihen ein \n zu viel. So wird aus:
( 1) | test | 22. 2.2222 | testort |
( 2) | test3 | 12. 1.2018 | testort3 |
( 3) | test5 | 11. 1.2018 | testort5 |
( 4) | test6 | 22. 1.2222 | testort6 |

Bei löschen von 3


( 1) | test | 22. 2.2222 | testort |
( 2) | test3 | 12. 1.2018 | testort3 | ( 3) | test6 | 22. 1.2222 | testort6 |



Und bei löschen von 1 fehlt die neue Nummerierung, welche bei löschen von allen anderen aber funktioniert..
 

German

Well-Known Member
c-b Experte
#4
Ich würde wahrscheinlich gleich zeilenweise lesen.

Ist ein bisschen kompliziert, was du da machst.
Probier mal
C:
                if (newlinezahler == aufgabe - 1) { // wenn die \n einer weniger sind als die zu löschende Aufgabe sind wir vor der zu löschenden Aufgabe
                    while ((c = fgetc(fpalt)) != '\n') { // die Zeichen bis zum nächsten \n werden nicht in die zweite Datei kopiert
                    }
                    if (aufgabe != 1)
                        fputc('\n', fp);
                    newlinezahler++;
                }
                else
                    fputc(c, fp);
 

German

Well-Known Member
c-b Experte
#5
Öhm ... machst du das stdin eigentlich leer, nach Eingabe von bestaetigung oder ist das '\n' vom Enter noch drin?
 
#6
eigentlich mache ich nach jedem scan mit clearscanf() alles leer...

Löschen funktioniert so alles gut, nur die nummerierung will er beim ersten nicht machen..
 
Zuletzt bearbeitet:

German

Well-Known Member
c-b Experte
#7
Ich dachte ich könnte deinen Code in den Compiler werfen und den Fehler suchen. Stattdessen...
Code:
||=== Erstellen: Release in test2_c (compiler: GNU GCC Compiler) ===|
main.c||In function 'eingabe':|
main.c|96|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|96|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|100|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|100|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|115|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|115|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|119|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c|119|warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[36]' [-Wformat=]|
main.c||In function 'dateiSchreiben':|
main.c|147|warning: implicit declaration of function 'aufgabenanzahl' [-Wimplicit-function-declaration]|
main.c||In function 'listeAusgeben':|
main.c|169|warning: unused variable 'count' [-Wunused-variable]|
||=== Build finished: 0 error(s), 10 warning(s) (0 minute(s), 3 second(s)) ===|
Mich wundert dass da bei dir überhaupt was passiert. Kannst du das erst mal bereinigen? Der der Name eines char-Arrays ist bereits ein Pointer auf char. Der Address-Of Operator macht hier einen falschen Pointer draus und dein Programm sollte bei Eingaben eigentlich crashen.
Wenn du eine Funktion an einer Stelle aufrufst, wo sie noch nicht bekannt ist, müsste auch das in die Hose gehen. Visual Studio scheint hier sehr tolerant zu sein.
Unused variable ist kein Beinbruch, aber prüfe mal ob du nicht doch damit arbeiten wolltest.
 

asc

Well-Known Member
c-b Experte
#8
Visual Studio scheint hier sehr tolerant zu sein.
Implizit deklarierte Funktionen sind erst seit C99 im Standard als fehlerhaft definiert. GCC hält sich nur nicht daran. Implizit deklariert muss die Funktion ein int zurückgeben und kann beliebige Argumente erhalten. Und da das bei aufgabenanzahl zutrifft funktioniert es.

Yep, ernsthaft. Ist halt C :D
 

German

Well-Known Member
c-b Experte
#10
Naja, irgendwie scheint der Code ja zu rennen. Ich bin nur erstaunt, dass man nicht immer zuerst allen Warnungen nachgeht und sie ausräumt (oder sie zumindest bewertet), wenn sich ein Programm nicht so verhält wie erwartet. Das ist der eigentliche Grund für meine Aufforderung das erst mal zu tun...
 
Oben