[C] Baza danych, lista łaczona

0

Witam wszystkich.
Mam do napisania program obsługi wypożyczalni płyt DVD. Program musi umożliwiać wykonanie: dodawania nowych płyt i klientów, wyszukiwanie, modyfikacja i usuwanie już istniejących oraz wypożyczanie. Bazy odczytywane i zapisywane do domyślnych plików.

Ponieważ program ma mieć nieskończenie wiele wpisów pomyślałem, żeby stworzyć listę łączoną, czyli, że każda struktura ma w sobie wskaźnik do następnej struktury. Strasznie gubię się w wskaźnikach, więc liczę na waszą pomoc.

Jest to mój trzeci program w życiu, więc proszę o wyrozumiałość :)

Napisałem część kodu odpowiadającą za dodawanie nowych płyt i klientów. I tu chyba wszystko działa. Lista istnieje i jest zapisywana do pliku.
Chciałem napisać funkcję do wyszukiwania określonej części listy. Mój pomysł: funkcja wczytuje z pliku dane i na nowo tworzy listę łączoną. I tu się robi problem.

struct DVD
{
char title[MAXT];
char category[MAXC]
float cost;
struct DVD *nast;
}
int szukaj()
{
struct DVD *glowny = NULL;
struct DVD *poprz, *biezacy;
struct DVD pierwszy;

daneplyt=fopen("plyty.c", "r");
while(fread(&pierwszy, sizeof(struct DVD), 1, daneplyt) == 1)
{
if(glowny == NULL)
{
glowny = &pierwszy;
biezacy = glowny;
}
else
{
biezacy=&pierwszy;
poprz->nast=biezacy;
}
biezacy -> nast= NULL;
poprz = biezacy;
printf("Film: %s \t Kategoria: %s \t cena: %0.2f\n", biezacy->title, biezacy->category, biezacy->cost);
}
fclose(daneplyt);
return(0);
} 

Za pierwszym razem pętla while działa dobrze. Wczytuje to, co było w pliku. Za każdym następnym razem program wariuje. Podejrzewam, że błąd jest w "else". Ponieważ całość jest strasznie zrobiona na czuja, nie wiem co to za błąd i jak go naprawić.
Druga sprawa to jak wyszukać w liście łączonej to, co chcę? Musi być jakieś porównanie tego, co wprowadzi użytkownik z tym, co jest w liście. Jakaś wskazówka?

Pozdrawiam i z góry dziękuję za pomoc.

0

Że tak spytam. Jaki jest sens tej listy w funkcji szukaj i dlaczego nie ma tam ani jednego wywołania funkcji malloc?

0
struct DVD
{
char title[MAXT];
char category[MAXC]
float cost;
struct DVD *nast;                  //<------ !
}
...
while(fread(&pierwszy, sizeof(struct DVD), 1, daneplyt) == 1)   // <----- auucc..
...
struct DVD *glowny = NULL;
struct DVD *poprz, *biezacy;
struct DVD pierwszy;                 //<---- !!!!!
...
glowny = &pierwszy;                 //<---- aaaauuuuuuucccccc...
biezacy = glowny;
...
0
0x666 napisał(a)

Że tak spytam. Jaki jest sens tej listy w funkcji szukaj i dlaczego nie ma tam ani jednego wywołania funkcji malloc?

Wydawało mi się, że lista musi zostać odtworzona by później wyszukać coś w niej. Malloc jeszcze dopiszę.

quetzalcoatl -> Mógłbyś to jakoś rozwinąć? Bo nie bardzo rozumiem gdzie błąd robię. Pierwszy blok informacji jest wczytywany do zmiennej pierwszy. Jej adres jest przypisywany do glowny, gdzie glowny to początek listy.

0

Wydawało mi się, że lista musi zostać odtworzona by później wyszukać coś w niej.

No tak, ale w twoim przykładzie widać, że ta lista odtwarzana jest tylko na potrzebę funkcji szukaj. Równie dobrze możesz wczytywać rekordy z pliku, porównywać i robić cokolwiek innego bez tej listy. Tę funkcję można skrócić do takiej formy:

int szukaj()
{
	struct DVD pierwszy;

	daneplyt=fopen("plyty.c", "r");
	while(fread(&pierwszy, sizeof(struct DVD), 1, daneplyt) == 1)
	{
		printf("Film: %s \t Kategoria: %s \t cena: %0.2f\n", pierwszy->title, pierwszy->category, pierwszy->cost);
	}
	
	fclose(daneplyt);
	return(0);
} 

efekt będzie ten sam ;)

0
creaton napisał(a)

quetzalcoatl -> Mógłbyś to jakoś rozwinąć? Bo nie bardzo rozumiem gdzie błąd robię. Pierwszy blok informacji jest wczytywany do zmiennej pierwszy. Jej adres jest przypisywany do glowny, gdzie glowny to początek listy.

pierwsze auc dotyczy fread, poniewaz najwyrazniej zapisales do pliku tekstowego strukture zawierajaca wskaznik. jak sie zachowa czytanie tego i nastepnego rekordu, jesli ten wskaznik bedzie mial wartosc 0x00333231 co po zapisie tekstowym da "123" albo jesli bedzie mial wartosc 0x00202020 czyli " "?

a co do tego drugiego auc, to zauwazyles ze za kazdym razem &pierwszy daje ten sam adres? co z tego wynika? gdzie laduja kolejne partie danych odczytane przez fread? jakie sa wartosci wskaznikow DVD::nast po "utworzeniu" listy?

0

pierwsze auc dotyczy fread, poniewaz najwyrazniej zapisales do pliku tekstowego strukture zawierajaca wskaznik. jak sie zachowa czytanie tego i nastepnego rekordu, jesli ten wskaznik bedzie mial wartosc 0x00333231 co po zapisie tekstowym da "123" albo jesli bedzie mial wartosc 0x00202020 czyli " "?

Masz rację, dopiero teraz zdałem sobie z tego sprawę o co chodziło. Zmieniłem więc kod i zamiast zapisywać całą strukturę, kolejno zapisuję jej elementy za pomocą fprintf(). (czy fputc() da taki sam efekt?)

Czyli dla struktury struct DVD dane w pliku wyglądają tak:

Szeregowiec Ryan
wojenny
10.00
Zielona mila
dramat
5.00

itd...

Aby móc wyszukiwać, modyfikować i usuwać rekordy z listy musi być teraz odtwarzana przy każdym uruchomieniu programu.

int szukajfilm()
{
    struct DVD *glowny = NULL;
    struct DVD *poprz, *biezacy;
    struct DVD pierwszy;
    char temp[TYMCZ];

    daneplyt=fopen("plyty.c", "r");
    while(fgets(temp, MAXT, daneplyt) != NULL)
    {
        biezacy = (struct DVD *) malloc(sizeof(struct DVD));
        if(glowny == NULL)
            glowny = biezacy;
        else
            poprz->nast=biezacy;
        biezacy->nast=NULL;
        strcpy(biezacy->title, temp);
        fgets(temp, MAXC, daneplyt);
        strcpy(biezacy->category, temp);
        fscanf(daneplyt, "%0.2f", &biezacy->cost);
        biezacy->nast=NULL;
        poprz=biezacy;
    }
    biezacy = glowny;
    while (biezacy != NULL)
    {
        printf("Film: %s kategoria: %s cena: %0.2f", biezacy->title, biezacy->category, biezacy->cost);
        biezacy = biezacy->nast;
    }
    fclose(daneplyt);
    return(0);
}

Napisałem coś takiego do odtwarzania listy. Część z printf służy tylko i wyłącznie do pokazania mi, że lista się prawidłowo odtwarza. No i jak się można domyślić jest błąd :)

Pierwsze dwie wartości się dobrze wczytują. Czyli dla przykładu wyżej:
Film: Szeregowiec Ryan, Kategoria: wojenny, Cena: error ;)
Domyślam się, że chodzi o fscanf.. Jak zrobić by cena była poprawnie wczytywana z pliku? Nie mogę korzystać tu ze fgets() bo on wczytuje tylko łańcuchy.

Proszę o pomoc.

0

dziwne.. fscanf wyglada prawidlowo, formatstring rowniez.. na pewno trzecia linijka pliku z danymi jest poprawna? moze jakies smieci sie wkradly.. dobrze tez jest sprawdzic co fscanf zwraca -- tak jak wszystkie z rodziny scanf, zwraca ilosc poprawnie odczytanych pól -- czyli tutaj powinien zwrocic jedynke. mozesz w miare prosto sprawdzic czy calosc wczytywania poza tym jednym jest poprawna -- tymczasowo zamien fscanf'a na fgets'a i odczytaj cala linie pomijajac w ten sposob cene. sprawdzisz czy cala reszta sie dobrze odczytuje, a nad cena - hm - dziwne :)

0

Sprawdziłem plik, wszystko wygląda tak jak napisałem wcześniej.

To co się dzieje po uruchomieniu tego:

Jak działa fscanf() to mam:
Film: Szeregowiec Ryan kategoria: wojenny cena: 0.00
Film: 10.00 Kategoria: Zielona mila cena: 0.00
Film: dramat kategoria: 5.00 cena: 0.00
itd..

Usunąłem fscanf() i zamiast tego dałem fgets()
Film: Szeregowiec Ryan kategoria: wojenny cena: 0.00
Film: Zielona mila kategoria: dramat cena: 0.00

0

sprawdzilem - prawidlowy fscanf w Twoim przypadku to:

fscanf(daneplyt, "%f ", &biezacy.cost)

tak, razem z ta spacja po %f !!

0

Edit:

Działa :)

fscanf(daneplyt, "%f ", &biezacy->cost);

I ładnie się ładuje, dzięki.
Ale jeszcze pewnie będę miał jakieś pytania ;)

PS: Mógłbyś jeszcze wytłumaczyć czemu spacja robi taką różnice w funkcjonowaniu programu?

0

po pierwsze, miales %0.2f co nie jest prawidlowym formatem dls SCANF i ... scanf nic nie czytal. stad pozniejsze "tytul: 10.00"

po drugie, bialy znak w formatstringu (spacja, tab, nowa linia ...) nakazuje pominiecie (wczytanie i olanie) wystepujacych w tym miejscu wszysktich bialych znakow.

powiedzmy ze miales plik:

aaaa\n
bbbb\n
10.5\n
ccccc\n
dddd\n
5.5\n

po wczytaniu 2x fgets:

10.5\n
ccccc\n
dddd\n
5.5\n

i teraz po

\n
ccccc\n
dddd\n
5.5\n

i jak widac, zostaje znak nowej linii.. jesli teraz doszloby do fgets'a ktory chcialby czytac nastepny rekord - to ten fgets odczytalby "\n". nastepny fgets od kategorii wczytalby "cccc\n" itd.

natomiast, jesli uzytoby wtedy scanf("%f ") (ze spacja!), to w pliku zostaloby

ccccc\n
dddd\n
5.5\n

bo wszystkie biale znaki po %f zostalyby wyciete. i teraz nastepujace po tym fgets'y zachowaja sie poprawnie

0

Ok, mam jeszcze parę pytań. W zasadzie dzisiaj okazało się, że programowanie mam już zaliczone, ale chciałbym oddać jeszcze ten program.

Możesz doradzić mi jakieś dobre metody wyszukiwania, modyfikacji i usuwania rekordów? Są jakieś prototypy funkcji?

Próbowałem stworzyć funkcję, która zapisuje w tablicy nazwę szukanego filmu, po czym za pomocą funkcji strcmp() porównywałby z każdą linią pliku, aż znajdzie. Problem jest taki, że gdy wczytuje z pliku za pomocą fgets() do jakiejś drugiej tablicy, to chyba wczytują się także znaki enteru i strcmp() niczego nie znajduje. Jeżeli korzystam z fscanf() to nie wczytuje się cały łańcuch tylko pierwszy jego fragment (do spacji) i także wychodzi później kicha.

0

tak, fgets tez wczytuje \n na koncu, a fscanf przerywa na pierwszym bialym znaku. to wlasnie ta roznica o ktorej mowilem wczesniej, i stad to procent-ef-spacja :)
proponuje uzywac fgets i wycinac \n recznie, latwa sprawa bo bedzie zawsze ostatnim znakiem w ciagu:

fgets(temp, MAXC, daneplyt);
temp[strlen(temp)-1]=0;

doradzic..? ciezko cos powiedziec.. kombinuj:)

0

Kombinuje, kombinuje..
Na razie idę po linii najmniejszego oporu i wszystkie operacje są robione na plikach.
Przy wyszukiwaniu nie ma problemu. Przy usuwaniu też nie - tworzę dodatkowy plik, zawartość jest kopiowana, oprócz tego, co chcemy usunąć i znowu kopiuję dane do pierwszego pliku. Modyfikację zrobię podobnie.

Nie jest to może najlepsza metoda, bo pochłania trochę pamięci na drugi plik, ale póki co program ma działać, a nie umiem wyszukiwać tego na liście łączonej. Te wskaźniki - bleh (teraz dopiero widzę, że w sumie odtwarzanie tej listy wcale nie było potrzebne. Ale jak już ją mam, to może dorzucę jakąś opcję do menu z tym).

Pojawił mi się problem z funkcją gets(). Gdy program prosi o podanie tytułu filmu do wyszukania, łańcuch jest pobierany za pomocą scanf() i jak wiadomo zjada to, co jest po spacji. A korzystam z tego, bo jakimś sposobem gets() jet zupełnie pomijany - tak jakby go w ogóle nie było. Jest to dziwne, bo jakieś 100 linijek kodu wyżej, gets() działa normalnie.. :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1