[Pascal] Odczyt lub (jeśli nie istnieje) utworzenie pliku


(Clank) #1

Witam

Nie było sensu dalej ciągnąć mojego poprzedniego tematu, ponieważ zrobiłby ię zbyt duży bałagan, więc zakładam nowy :wink: Robię bazę danych zawierającą informacje na temat rezerwacji w hotelu. To, czego potrzebuję, to między innymi plik prechowujący zmienną, która służy jako wskaźnik do następnego rekordu w pliku ‘rezerwacje.dat’ Po wykonaniu programu wyskakuje błąd exitcode 100. Poniżej zamieszczam problematyczne elementy programu:

http://wklej.org/id/917782/


(Pablo_Wawa) #2

A że tak powiem z innej strony - po co w ogóle trzymasz ten wskaźnik? Czy nie wystarczy po prostu odczytać wielkość pliku z danymi (‘rezerwacje.dat’) i na tej podstawie określić liczbę jego rekordów?


(Clank) #3

Fajnie by było, gdybym wiedział jak to zrobić. :smiley: No nic spróbuję poszukać czegoś na ten temat, jeśli nie znajdę nic to się odezwę.


(Pablo_Wawa) #4

Patrzę na ten Twój program i wydaje mi się, że powinieneś wstrzymać się z kodowaniem do czasu zrobienia “porządków”. Pisanie programu nie polega na przerabianiu go tak, by kompilator przestał wywalać błędy, a potem by podczas jego pracy błędy nie występowały. Sugeruję, byś przygotował małą dokumentację swojego programu (brzmi groźnie), tj. żebyś opisał kluczowe zmienne do czego mają w programie służyć (np. jako komentarze obok ich definicji), żebyś opisał co ma robić każda z funkcji (i procedur), tj. co ma robić, na jakich danych operuje, co zmienia, itp. (to możesz też zrobić w formie komentarzy przed każdą funkcją/procedurą). To spowoduje, że uporządkujesz myślenie i będziesz mógł zająć się poprawą poszczególnych funkcji a potem będziesz w stanie złożyć z nich cały program.

EDIT: co do Twojego kodu i upartej walki z operacjami wejścia/wyjścia (I/O), to zajrzyj na tę stronę http://www.freepascal.org/docs-html/rtl/system/ioresult.html i zobacz, jak się poprawnie sprawdza, czy plik istnieje - kluczową sprawą jest w tym wypadku wyłączenie sprawdzania błędów I/O dyrektywą {$i-} a następnie jej włączenie {$i+} i sprawdzenie czy nie ma błędu.


(Clank) #5

Ok jestem coraz bliżej rozwiązania sprawy, więc może pojedynczo będę prosił o poprawienie procedur. A więc mam taki problem jak poniżej - czy da się coś zrobić, by w miejsce break’a wstawić coś, co przeskakiwałoby do końca tej procedury poza case’y (instrukcja goto też mi nie chciała działać, bo nie może jej być w tym miejscu)?

procedure sprawdz_wolne;


        begin

        clrscr;

        writeln ('Podaj dzien rezerwacji:');

        readln (dzien);

        writeln ('Podaj miesiac rezerwacji:');

        readln (miesiac);

        writeln ('Podaj dlugosc trwania rezerwacji:');

        readln (ilosc_dni);

                CASE miesiac of

                1: data:=dzien;

                2: data:=31+dzien;

                3: data:=31+28+dzien;

                4: data:=2*31+28+dzien;

                5: data:=2*31+30+28+dzien;

                6: data:=3*31+30+28+dzien;

                7: data:=3*31+2*30+28+dzien;

                8: data:=4*31+2*30+28+dzien;

                9: data:=5*31+2*30+28+dzien;

                10: data:=5*31+3*30+28+dzien;

                11: data:=6*31+3*30+28+dzien;

                12: data:=6*31+4*30+28+dzien;

                end;

        writeln ('Podaj ilosc osob');

        readln (ilosc_osob);

        CASE ilosc_osob of

        1: begin

           assign(pokoj1, 'pokoj1.dat');

           reset(pokoj1);

           for i:=1 to ilosc_dni do

                begin

                if (kalendarz_pokoj1[data]=1) then

                      break

                else

                data:=data+1;

                end;

           data:=data-ilosc_dni;

           for i:=1 to ilosc_dni do

                begin

                kalendarz_pokoj1[data]:=1;

                write(pokoj1, kalendarz_pokoj1[data]);

                data:=data+1;

                end;

           close(pokoj1);

           decyzja;

           end;

        2: begin

           assign(pokoj2, 'pokoj2.dat');

           reset(pokoj2);

           for i:=1 to ilosc_dni do

                begin

                if (kalendarz_pokoj2[data]=1) then

                      break

                else data:=data+1;

                end;

             data:=data-ilosc_dni;

           for i:=1 to ilosc_dni do

                begin

                kalendarz_pokoj2[data]:=1;

                write(pokoj2, kalendarz_pokoj2[data]);

                data:=data+1;

                end;

           close(pokoj2);

           decyzja;

           end;

        3: begin

           assign(pokoj3, 'pokoj3.dat');

           reset(pokoj3);

           for i:=1 to ilosc_dni do

                begin

                if (kalendarz_pokoj3[data]=1) then

                      break

                else data:=data+1;

                end;

             data:=data-ilosc_dni;

           for i:=1 to ilosc_dni do

                begin

                kalendarz_pokoj1[data]:=1;

                write(pokoj3, kalendarz_pokoj3[data]);

                data:=data+1;

                end;

           close(pokoj3);

           decyzja;

           end;

        4: begin

           assign(pokoj4, 'pokoj4.dat');

           reset(pokoj4);

           for i:=1 to ilosc_dni do

                begin

                if (kalendarz_pokoj4[data]=1) then

                      break

                else data:=data+1;

                end;

             data:=data-ilosc_dni;

           for i:=1 to ilosc_dni do

                begin

                kalendarz_pokoj1[data]:=1;

                write(pokoj4, kalendarz_pokoj4[data]);

                data:=data+1;

                end;

           close(pokoj4);

           decyzja;

           end;

           end;

           writeln('Brak wolnych miejsc/odrzucono propozycje rezerwacji');

           readln;

        end;

([alex]) #6

zamiast:

case miesiac …

zrób przed begin:

const M:array[1…12]of Integer=(0,31,31+28,2*31+28,2*31+30+28,3*31+30+28, …);

zaś zamiast samego case wstaw:

data:=M[miesiac]+dzien;

Z tym break’em wynieś wszystko co masz przed tym miejscem do którego chcesz wskoczyć do procedury lub funkcji, zaś zamiast break dajesz exit;


(Pablo_Wawa) #7

Co do powyższego - nie wziąłeś pod uwagę roku 2016 - jest to rok przestępny (luty ma 29 dni) - a także lat następnych - takie błędy wyjdą za kilka lat, kiedy program się rozrośnie i będzie ■■■■. http://pl.wikipedia.org/wiki/Rok_przest%C4%99pny

Jak się tak popatrzy na Twój kod, to widać w nim powielone te same instrukcje, a to można ładnie ogarnąć jak się zastosuje np. tablice:

  • zarówno dla nazw plików:

    const plik.pokoje: array[1…4] of string=(‘pokoj1.dat’,‘pokoj2.dat’,‘pokoj3.dat’,‘pokoj4.dat’);

lub nawet jako użycie konstrukcji ‘pokoj’+ilosc_osob+’.dat’ i potem możesz użyć np. assign(pokoj,plik.pokoje[ilosc_osob]); - jak i kalendarza:

var kalendarz_pokoj[1..4][1..366] of boolean/byte;

i używanie konstrukcji

if (kalendarz_pokoj[ilosc_osob][data]=1) then

Co Ci skróci 4-krotnie cały kod i zrobi go czytelniejszym oraz bardziej odpornym na błędy (bo jak będziesz coś zmieniał w jednej części (case), to musisz to teraz robić w pozostałych trzech. A kiedy będziesz chciał rozszerzyć program do obsługi pokoi 5-osobowych, wtedy wystarczy zmienić stałe w deklaracjach tablic (przy okazji warto zdefiniować sobie stałą

const MAX_LICZBA_OSOB = 4;

i używać jej w programie i definicjach zakresu tablic, a kod pisać tak, jakby chodziło o pokój n-osobowy, gdzie n jest od 1 do MAX_LICZBA_OSOB, przez co obsłużysz wszystkie możliwości za jednym zamachem.

P.S. I pisz tak, żeby nie trzeba było korzystać z instrukcji goto!


(Clank) #8

Dzięki Pablo, już wszystko ogarnąłem tylko jeszcze jednej rzeczy nie zawarłem w swoim projekcie (typów proceduralnych), o których napiszę w następnym temacie :x