Nie jesteś zalogowany.
Jeśli nie posiadasz konta, zarejestruj je już teraz! Pozwoli Ci ono w pełni korzystać z naszego serwisu. Spamerom dziękujemy!

Ogłoszenie

Prosimy o pomoc dla małej Julki — przekaż 1% podatku na Fundację Dzieciom „Zdążyć z Pomocą”.
Więcej informacji na dug.net.pl/pomagamy/.

#1 2018-03-04 14:01:58

hubot
Użytkownik
Zarejestrowany: 2017-04-13

Zadanie 14.7 z C z książki Stephena Praty

Witam.

Mam problem z rozwiązaniem zadania 7 z rozdziału 14.

Zmodyfikuj listing 14.14 tak, aby w miarę odczytywania kolejnych rekordów i wyświetlania ich na ekranie możliwe było usunięcie lub zmiana zawartości każdego rekordu. W przypadku usunięcia rekordu w zwolnionym miejscu tablicy powinien zostać umieszczony następny odczytany rekord. Aby umożliwić zmianę zawartości pliku, będziesz musiał użyć trybu "r+b" zamiast "a+b". Będziesz również musiał poświęcić więcej uwagi wskaźnikowi położenia, tak aby dodawane rekordy nie zamazywały rekordów istniejących. Najprostrszym wyjściem jest przygotowanie całości danych w pamięci komputera, a następnie zapisanie ich w ostatecznej wersji w pliku. Usuwanie można by obsłużyć poprzez uzupełnienie struktury o specjalny znacznik, sygnalizujący, że struktura została usunięta (i nie ma być zapisana do pliku).

Mój kod:

Kod:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXTYT 40
#define MAXAUT 40
#define MAXKS 10

char * wczytaj(char * z, int ile);

struct ksiazka {
    char tytul[MAXTYT];
    char autor[MAXAUT];
    float wartosc;
    bool usunieta;
};

int main(void) {
    struct ksiazka bibl[MAXKS];
    int licznik = 0;
    int index, licznikp;
    FILE * pksiazki;
    int rozmiar = sizeof(struct ksiazka);
    if ((pksiazki = fopen("ksiazki.dat", "a+b")) == NULL) {
        fputs("Nie moge otworzyc pliku ksiazki.dat\n", stderr);
        exit(1);
    }
    rewind(pksiazki);
    while (licznik < MAXKS && fread(&bibl[licznik], rozmiar, 1, pksiazki) == 1) {
        if (licznik == 0)
            puts("Biezaca zawartosc pliku ksiazki.dat:");
        printf("%s by %s: %.2f zl\n", bibl[licznik].tytul, bibl[licznik].autor,
               bibl[licznik].wartosc);
        licznik++;
    }
    licznikp = licznik;
    if (licznik == MAXKS) {
        fputs("Plik ksiazki.dat jest pelny.", stderr);
        exit(2);
    }
    struct ksiazka ksiazka;
    int wybor;
    puts("Co chcesz zrobic (1 - dodac, 2 - usunac, 3 - zmodyfikowac, 4 - zostawic bez zmian)?");
    scanf("%d", &wybor);
    switch (wybor) {
    case 1:
        puts("Podaj nowe tytuly ksiazek.");
        puts("Aby zakonczyc, nacisnij [enter] na poczatku wiersza.");
        while (licznik < MAXKS && wczytaj(bibl[licznik].tytul, MAXTYT) != NULL
               && bibl[licznik].tytul[0] != '\0') {
            puts("Teraz podaj autora.");
            wczytaj(bibl[licznik].autor, MAXAUT);
            puts("Teraz podaj wartosc.");
            scanf("%f", &bibl[licznik++].wartosc);
            while (getchar() != '\n')
                continue;
            bibl[licznik].usunieta = false;
            if (licznik < MAXKS)
                puts("Podaj nastepny tytul.");
        }
        break;
    case 2:
        puts("Podaj tytul ksiazki do usuniecia.");
        wczytaj(ksiazka.tytul, MAXTYT);
        puts("Teraz podaj autora.");
        wczytaj(ksiazka.autor, MAXAUT);
        puts("Teraz podaj wartosc.");
        scanf("%f", &ksiazka.wartosc);
        int i = 0;
        while (i < MAXKS && strcmp(bibl[i].tytul, ksiazka.tytul) != 0
               && strcmp(bibl[i].autor, ksiazka.autor) != 0
               && bibl[i].wartosc != ksiazka.wartosc)
            i++;
        if (i != MAXKS)
            bibl[i].usunieta = true;
        break;
    default:
        puts("Nieprawidlowy wybor.");
        break;
    }

    if (licznik > 0) {
        puts("Oto lista Twoich ksiazek:\n");
        for (index = 0; index < licznik; index++)
            if (!bibl[index].usunieta) {
                printf("%s, autor: %s, cena: %.2f zl\n", bibl[index].tytul,
                   bibl[index].autor, bibl[index].wartosc);
                fwrite(&bibl[licznikp], rozmiar, licznik - licznikp, pksiazki);
            }
    } else
        puts("Zadnych ksiazek? Szkoda\n");
    puts("Koniec.\n");
    return 0;
}

char * wczytaj(char * z, int ile) {
    char * wynik;
    char * tutaj;
    wynik = fgets(z, ile, stdin);
    if (wynik) {
        tutaj = strchr(z, '\n');
        if (tutaj)
            *tutaj = '\0';
        else
            while (getchar() != '\n')
                continue;
    }
    return wynik;
}

Wychodzą mi dziwne wyniki po uruchomieniu programu:

Biezaca zawartosc pliku ksiazki.dat:
Metryczna mlodosc by POlly Poetica: 75.99 zl
Co chcesz zrobic (1 - dodac, 2 - usunac, 3 - zmodyfikowac, 4 - zostawic bez zmian)?
1
Podaj nowe tytuly ksiazek.
Aby zakonczyc, nacisnij [enter] na poczatku wiersza.
Oto lista Twoich ksiazek:

Metryczna mlodosc, autor: POlly Poetica, cena: 75.99 zl
Koniec.

Czemu zczytanie jedynki powoduje, że nie chce się wczytać tytuł?

Offline

 

#2 2018-03-04 15:51:48

Elizabeth
Użytkownik
Zarejestrowany: 2017-12-27

Re: Zadanie 14.7 z C z książki Stephena Praty

hubot napisał(-a):

Czemu zczytanie jedynki powoduje, że nie chce się wczytać tytuł?

Ponieważ funckja wczytaj zwraca za pierwszym razem pustego stringa (o długości 0) i dlatego ta pętla:

Kod:

while (licznik < MAXKS && wczytaj(bibl[licznik].tytul, MAXTYT) != NULL
               && bibl[licznik].tytul[0] != '\0') { 
(...)

nigdy się nie wykona

można po prostu przed rozpoczęciem pętli raz wywołać:

wczytaj(bibl[licznik].tytul, MAXTYT)

i będzie działać tak jak chciał autor


GPG Key ID: 0x8D55F13761AF5230
Fingerprint: B884 468A D6DC 0516 2B43 6675 8D55 F137 61AF 5230

Offline

 

#3 2018-03-04 17:11:32

Elizabeth
Użytkownik
Zarejestrowany: 2017-12-27

Re: Zadanie 14.7 z C z książki Stephena Praty

Tak się dzieje ponieważ scanf nie wczytuje newline i jest on zbierany przez pierwsze wywołanie fgets w funkcji wczytaj

Ostatnio edytowany przez Elizabeth (2018-03-04 18:30:52)


GPG Key ID: 0x8D55F13761AF5230
Fingerprint: B884 468A D6DC 0516 2B43 6675 8D55 F137 61AF 5230

Offline

 

#4 2018-03-13 19:36:36

hubot
Użytkownik
Zarejestrowany: 2017-04-13

Re: Zadanie 14.7 z C z książki Stephena Praty

Mam problem z usuwaniem rekordów z pliku.

Na początku otrzymuję wynik porządany:

Biezaca zawartosc pliku ksiazki.dat:
abc by def: 123.00 zl
abg by deh: 234.00 zl
Co chcesz zrobic (1 - dodac, 2 - usunac, 3 - zmodyfikowac, 4 - zostawic bez zmian)?
2
Podaj tytul ksiazki do usuniecia.
abg
Teraz podaj autora.
deh
Teraz podaj wartosc.
234.00
Oto lista Twoich ksiazek:

abc, autor: def, cena: 123.00 zl
Koniec.

Natomiast po ponownym uruchomieniu programu okazuje się, że rekord nie został usunięty:

Biezaca zawartosc pliku ksiazki.dat:
abc by def: 123.00 zl
abg by deh: 234.00 zl
Co chcesz zrobic (1 - dodac, 2 - usunac, 3 - zmodyfikowac, 4 - zostawic bez zmian)?

Mój kod:

Kod:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXTYT 40
#define MAXAUT 40
#define MAXKS 10

char * wczytaj(char * z, int ile);

struct ksiazka {
    char tytul[MAXTYT];
    char autor[MAXAUT];
    float wartosc;
    bool usunieta;
};

int main(void) {
    struct ksiazka bibl[MAXKS];
    int licznik = 0;
    int index, licznikp;
    FILE * pksiazki;
    int rozmiar = sizeof(struct ksiazka);
    if ((pksiazki = fopen("ksiazki.dat", "r+b")) == NULL) {
        fputs("Nie moge otworzyc pliku ksiazki.dat\n", stderr);
        exit(1);
    }
    rewind(pksiazki);
    while (licznik < MAXKS && fread(&bibl[licznik], rozmiar, 1, pksiazki) == 1) {
        if (licznik == 0)
            puts("Biezaca zawartosc pliku ksiazki.dat:");
        if (!bibl[licznik].usunieta)
            printf("%s by %s: %.2f zl\n", bibl[licznik].tytul, bibl[licznik].autor,
               bibl[licznik].wartosc);
        licznik++;
    }
    licznikp = licznik;
    if (licznik == MAXKS) {
        fputs("Plik ksiazki.dat jest pelny.", stderr);
        exit(2);
    }
    struct ksiazka ksiazka;
    int wybor;
    puts("Co chcesz zrobic (1 - dodac, 2 - usunac, 3 - zmodyfikowac, 4 - zostawic bez zmian)?");
    scanf("%d", &wybor);
    while (getchar() != '\n') continue;
    switch (wybor) {
    case 1:
        puts("Podaj nowe tytuly ksiazek.");
        puts("Aby zakonczyc, nacisnij [enter] na poczatku wiersza.");
        while (licznik < MAXKS && wczytaj(bibl[licznik].tytul, MAXTYT) != NULL
               && bibl[licznik].tytul[0] != '\0') {
            puts("Teraz podaj autora.");
            wczytaj(bibl[licznik].autor, MAXAUT);
            puts("Teraz podaj wartosc.");
            scanf("%f", &bibl[licznik++].wartosc);
            while (getchar() != '\n')
                continue;
            bibl[licznik].usunieta = false;
            if (licznik < MAXKS)
                puts("Podaj nastepny tytul.");
        }
        break;
    case 2:
        puts("Podaj tytul ksiazki do usuniecia.");
        wczytaj(ksiazka.tytul, MAXTYT);
        puts("Teraz podaj autora.");
        wczytaj(ksiazka.autor, MAXAUT);
        puts("Teraz podaj wartosc.");
        scanf("%f", &ksiazka.wartosc);
        int i = 0;
        while (i < MAXKS && strcmp(bibl[i].tytul, ksiazka.tytul) != 0
               && strcmp(bibl[i].autor, ksiazka.autor) != 0
               && bibl[i].wartosc != ksiazka.wartosc)
            i++;
        if (i != MAXKS)
            bibl[i].usunieta = true;
        break;
    default:
        puts("Nieprawidlowy wybor.");
        break;
    }

    if (licznik > 0) {
        puts("Oto lista Twoich ksiazek:\n");
        for (index = 0; index < licznik; index++)
            if (!bibl[index].usunieta) {
                printf("%s, autor: %s, cena: %.2f zl\n", bibl[index].tytul,
                   bibl[index].autor, bibl[index].wartosc);
            }
        fwrite(&bibl[licznikp], rozmiar, licznik - licznikp, pksiazki);
    } else
        puts("Zadnych ksiazek? Szkoda\n");
    puts("Koniec.\n");
    return 0;
}

char * wczytaj(char * z, int ile) {
    char * wynik;
    char * tutaj;
    wynik = fgets(z, ile, stdin);
    if (wynik) {
        tutaj = strchr(z, '\n');
        if (tutaj)
            *tutaj = '\0';
        else
            while (getchar() != '\n')
                continue;
    }
    return wynik;
}

Offline

 

#5 2018-07-14 02:15:03

091619EE
Użytkownik
Zarejestrowany: 2018-07-09

Re: Zadanie 14.7 z C z książki Stephena Praty

O matko, jak można tak "usuwać" dane z tablicy poprzez zaznaczanie flagi ??? Powinieneś po prostu przepisywać dane do nowej tablicy pomijając elemnety spełniające warunek usunięcia, potem zwolnić dane oryginalne, a nowe dane podpiąć pod stary wskaźnik

Taka konstrukcja:

fwrite(&bibl[licznikp], rozmiar, licznik - licznikp, pksiazki);

akurat działa przy dodawaniau, ponieważ przy dodawaniu licznik się zwiększa a licznikp zachowuje oryginalną
wartość,

to nie może działać przy usuwaniu z wielu powodów, np. dlatego, że nigdzie w prgramie już nie jest cofany wskaźnik pozycji
pisania do pliku

najlepiej i najprosciej jest przepisac wszystkie dane do tablicy, zmodyfikowac tablice w programie, a pozniej zawsze na koncu, wyzerowac plik (np. otwierajac jeszcze raz z flaga 0_TRUNC) i wpisac cala tablice od początku:

fwrite(bibl, rozmiar, ilosc_elementow_po_modyfikacjach, pksiazki);

Offline

 

Stopka forum

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson
Możesz wyłączyć AdBlock — tu nie ma reklam ;-)