Programowanie strukturalne - pliki 1 LEKCJA 29. TYPY STRUKTURALNE - PLIKI Wprowadzenie Zmienne proste, tablice i rekordy są wykorzystywane w programie do przechowywania danych podczas wykonywania programu; nie zachowują swoich wartości po jego zakończeniu. Przydzielana dla tych obiektów pamięć jest zwalniana po zakończeniu wykonywania programu. Dane, które chcemy przechowywać, aby były dostępne po pewnym czasie, można przechowywać w pliku, który jest tworzony w pamięci zewnętrznej (np. na dysku). Do obsługi fizycznych zbiorów danych w programie pascalowym stosuje się pliki, które są logicznymi modelami zbiorów fizycznych. Plik pascalowy jest strukturą jednorodną, składającą się z sekwencji obiektów tego samego typu. Jest zmienną strukturalną, której długość może się zmieniać w trakcie wykonywania programu. Dotychczas posługiwaliśmy się dwoma plikami zewnętrznymi: Input i Output. Składowe pliku nie mają nazw i nie są bezpośrednio dostępne. Składowe te są ułożone w sekwencję, a program w danej chwili może mieć dostęp do tylko jednej składowej. W Pascalu wyróżniamy trzy typy plików: - zdefiniowane - tekstowe - niezdefiniowane. Definicja typu plikowego a) zdefiniowanego identyfikator_typu = FILE OF opis_typu_elementów_pliku; PRZYKŁAD 1. TYPE Liczby = FILE OF Integer; PRZYKŁAD 2. TYPE Wiersz = ARRAY [1..80] OF Char; PlikWierszy = FILE OF Wiersz; PRZYKŁAD 3. TYPE Ksiazka = RECORD nazwisko : String [20]; imię : String [12]; tytul : String [50]; nr_ksiazki: 1..1000; wydawca : String[30]; wydanie : 1..100 Katalog = FILE OF Ksiazka; Wszystkie zmienne typu plikowego, z wyjątkiem Output i Input muszą być zadeklarowane w sekcji deklaracji programu, który z nich korzysta.
Programowanie strukturalne - pliki 2 PRZYKŁAD 4. VAR l: liczby; tekst: PlikWierszy; moje_ksiazki: Katalog; b) tekstowego Plik tekstowy jest określany za pomocą predefiniowanego identyfikatora Text. PRZYKŁAD VAR plik: Text; c) niezdefiniowanego. identyfikator_typu = FILE; Przetwarzanie plików Dostęp do pliku uzyskuje się za pomocą zmiennej plikowej (tj. zmiennej zadeklarowanej jako zmienna typu plikowego). Przed użyciem takiej zmiennej (oprócz Input i Output) do przetwarzania pliku powinna ona być skojarzona z fizycznym zbiorem danych. Przed przystąpieniem do przetwarzania tego zbioru należy jeszcze zmienną plikową "otworzyć" a po zakończeniu przetwarzania "zamknąć". Przetwarzanie plików: 1. zakładanie (tj. tworzenie) nowego fizycznego zbioru danych, które będziemy nazywać zakładaniem pliku 2. wprowadzanie do założonego pliku elementów 3. dodawanie elementów do pliku już istniejącego 4. przeszukiwanie pliku 5. wyprowadzanie elementów z pliku 6. wymiana elementów pliku. Schemat przetwarzania pliku opis zmiennej plikowej (w części opisowej) skojarzenie pliku z fizycznym zbiorem otwarcie pliku wykonanie operacji na pliku zamknięcie pliku
Programowanie strukturalne - pliki 3 Kojarzenie zmiennej plikowej Do skojarzenia zmiennej plikowej z fizycznym zbiorem danych służy procedura standardowa Assign, której wywołanie ma postać: Assign (identyfikator_pliku, wyrażenie łańcuchowe) gdzie identyfikator_pliku - dowolna zmienna plikowa wyrażenie łańcuchowe - fizyczny zbiór danych Plik o podanym identyfikatorze nie może być otwarty. PRZYKŁAD 5. VAR moje_ksiazki: Katalog; plik1, plik2: Tekst; W programie: Assign (moje_ksiazki, 'a:\pliki\kat.doc'); PRZYKŁAD 6. Assign (plik1, 'LPT1'); - skojarzenie pliku logicznego plik1 z drukarką. Procedury Write, Writeln odnoszące się do pliku plik1 będą wyprowadzane na drukarkę. PRZYKŁAD 7. Assign (plik2, ''); - skojarzenie pliku logicznego plik2 ze standardowym WE/WY, w zależności od tego, czy plik będzie otwarty do odczytu czy do zapisu (równoważne z Assign (plik2, 'CON')). Otwieranie pliku W zależności od kierunku przesyłania elementów do lub z pliku stosowane są trzy standardowe procedury jego otwarcia. a) Jeżeli tworzymy nowy fizyczny (zewnętrzny) zbiór danych, to należy zastosować procedurę Rewrite: Rewrite (zmienna_plikowa) lub Rewrite (zmienna_plikowa, rozmiar_zapisu) - dla pliku niezdefiniowanego. Wywołanie procedury Rewrite powoduje utworzenie nowego fizycznego zbioru danych o nazwie skojarzonej z podaną zmienną plikową (przedtem musi być wywołana procedura Assign). Jeżeli zewnętrzny zbiór danych o takiej nazwie istnieje, to zostanie on usunięty i w jego miejsce będzie utworzony nowy zbiór. PRZYKŁAD 8. Assign (moje_ksiazki, 'a:\ksiazki.txt'); Rewrite (moje_ksiazki); po skojarzeniu zbioru ksiazki.txt ze zmienną plikową moje_ksiazki, następuje otwarcie pliku.
Programowanie strukturalne - pliki 4 b) Do otwarcia pliku skojarzonego ze zbiorem już istniejącym służą procedury Reset i Append. Reset (zmienna_plikowa) lub Reset (zmienna_plikowa, rozmiar_zapisu) - powoduje ustawienie wskaźnika zewnętrznego zbioru przed jego pierwszym elementem. c) dla plików tekstowych Append (zmienna_plikowa) - stosujemy tylko dla plików tekstowych (Text), po otwarciu pliku ustawienie zbioru na jego końcu. PRZYKŁAD 9. VAR plik: Kartoteka; Assign (plik, 'c:\zbiory\ksiazki.txt'); Reset (plik); END. PRZYKŁAD 10. VAR plik: Text; Assign (plik, 'c:\zbiory\teksty.txt'); Append (plik); END. Zmykanie plików Po wykonaniu wszystkich operacji na pliku, należy go zamknąć. Do zamykania pliku służy procedura Close: Close (zmienna_plikowa) Po zamknięciu pliku można zmienną plikową skojarzyć z innym fizycznym zbiorem danych. Nie zamknięcie pliku spowoduje utratę danych. Przykład obsługi plików VAR plik: Kartoteka; Assign (plik, 'a:\dane\ksiazki.txt'); Rewrite (plik); {teraz procedury wpisywania do pliku} Close (plik); Reset (plik); {teraz procedury czytania z pliku i np. wydruki na ekran} Close (plik); Assign (plik, 'LPT1');
Programowanie strukturalne - pliki 5 Rewrite (plik); {teraz procedury wydruku na drukarce} Close (plik) END. Podsumowanie Assign - skojarzenie pliku logicznego z fizycznym zbiorem Rewrite - otwarcie pliku do zapisu Reset - otwarcie pliku do odczytu Append - otwarcie pliku tekstowego Close - zamknięcie pliku
Programowanie strukturalne - pliki 6 LEKCJA 30. PRZETWARZANIE PLIKÓW Wprowadzanie elementów do pliku a) pliki tekstowe Write (zmienna_plikowa, lista_argumentów_wy) lub Writeln (zmienna_plikowa, lista_argumentów_wy) gdzie zmienna_plikowa jest identyfikatorem pliku tekstowego, a lista_argumentów_wy jak dla Output. Powyższe wywołania stosuje się najczęściej przy wyprowadzaniu wyników programu na drukarkę lub do pliku dyskowego, w celu późniejszego ich odczytu. PRZYKŁAD 1 Omówienie programu Przyklad1 (Pliki1.pas) PROGRAM Przyklad1; {pisanie do plikow tekstowych} USES Crt; CONST sciezka = 'c:\dane.dan'; {sciezka = 'LPT1';} VAR druk: Text; a: ARRAY[1..20,1..20] OF Real; n, m, i, j : Byte; PROCEDURE Liczba (VAR i: Byte; Max: Byte); {wczytanie liczby naturalnej x z kontrola poprawności danych} VAR x, y : Byte; x := WhereX; y := WhereY; Window(x,y,80,y); REPEAT ClrScr; Readln(i) UNTIL (0 < i) AND (i <= Max); Window (1,1,80,25); GoToXY (x, y); Writeln (i); {wymiar} ClrScr; Writeln ('Podaj wymiary macierzy: '); Write ('ilosc wierszy: '); Liczba (n,20); {wczytanie wymiarow macierzy}
Programowanie strukturalne - pliki 7 Write ('ilosc kolumn: '); Liczba (m,20); Assign (druk, sciezka); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} Rewrite (druk); {otwarcie pliku do zapisu} FOR i:=1 TO n DO FOR j:=1 TO m DO Write ('a[', i, ',', j,']='); Readln (a[i, j]); {wczytanie elementow macierzy a} {wyprowadzenie na drukarke napisow i wartosci} Writeln (druk, 'a[', i, ',', j, ']=', a[i, j]) Close (druk); ClrScr END. b) pliki zdefiniowane Write (zmienna_plikowa, lista_argumentów_wy) gdzie zmienna_plikowa - plik logiczny skojarzony z zewnętrznym zbiorem danych (otwarty) lista_zmiennych_wy - zmienne tego samego typu, jak typ elementów pliku, oddzielone przecinkami. PRZYKŁAD 2 Omówienie programu: Przyklad2 (Pliki2.pas) PROGRAM Przyklad2; {pisanie do plikow zdefiniowanych} CONST sciezka = 'c:\ksiazki.dan'; TYPE Ksiazka = RECORD numer: 1..1000; autor: String [30]; tytul: String [50] VAR katalog: FILE OF Ksiazka; dane: Ksiazka; Assign (katalog, sciezka); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} Rewrite (katalog); {otwarcie pliku do zapisu} WITH dane DO {wczytanie danych o ksiazce do zmiennej dane} Writeln; Write ('Numer: ');
Programowanie strukturalne - pliki 8 Readln (numer); Write ('Autor: '); Readln (autor); Write ('Tytul: '); Readln (tytul) Write (katalog, dane); {zapisane rekordu dane do pliku} Close (katalog); {zamkniecie pliku} ClrScr END. c) pliki niezdefiniowane BlockWrite (zmienna_plikowa, bufor, licznik); lub BlockWrite (zmienna_plikowa, bufor, licznik, wynik); gdzie bufor - dowolna zmienna licznik - wyrażenie typu Word, określające ilość wyprowadzanych zapisów wynik - zmienna typu Word, określająca ilość faktycznie wyprowadzonych na dysk zapisów. Podczas otwarcia pliku (Rewrite) należy podawać rozmiar zapisu (np. 1); domyślna wartość 128. PRZYKŁAD 3. Omówienie programu: Przyklad3 (pliki3.pas) PROGRAM Przyklad3; {pisanie do plikow niezdefiniowanych} VAR zbior: FILE; zapis1: RECORD znak: Char; liczba: Byte zapis2: RECORD znak: Char; tekst: String[100] wynik : Word; Assign (zbior, 'zb1.txt'); Rewrite (zbior, 2); WITH zapis1 DO Write ('Podaj znak: '); Readln (znak); Write ('Podaj liczbe: '); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} {otwarcie pliku do zapisu} {wczytanie danych}
Programowanie strukturalne - pliki 9 Readln (liczba) BlockWrite (zbior, zapis1, 1); {zapis do pliku} Close (zbior); {zamkniecie pliku} Assign (zbior, 'zb2.txt'); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} Rewrite (zbior, 102); {otwarcie pliku do zapisu} WITH zapis2 DO {wczytanie danych} Write ('Podaj znak: '); Readln (znak); Write ('Podaj ciag znakow: '); Readln (tekst) BlockWrite (zbior, zapis2, 1); {zapis do pliku} Close (zbior); {zamkniecie pliku} Assign (zbior, 'zb3.txt'); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} Rewrite (zbior, 1); {otwarcie pliku do zapisu} BlockWrite (zbior, zapis2, 102,wynik); {zapis do pliku} Close (zbior); {zamkniecie pliku} Writeln ('Zapisano: ',wynik); Readln; ClrScr END. Przeszukiwanie i wyprowadzanie elementów z pliku b) pliki tekstowe Read (zmienna_plikowa, lista_zmiennych_we); lub Readln (zmienna_plikowa, lista_zmiennych_we); gdzie zmienna_plikowa jest identyfikatorem pliku tekstowego, a lista_argumentów_wy jak dla Input. Zastosowanie: odczyt zbiorów tekstowych z dysku. PRZYKŁAD 4. Omówienie programu Przyklad4 (Pliki4.pas) PROGRAM Przyklad4; {czytanie z plikow tekstowych} VAR t: String; tekst: Text; Assign (tekst,'tekst1.txt'); Reset (tekst); {otwarcie pliku do odczytu}
Programowanie strukturalne - pliki 10 Readln (tekst, t); Close (tekst); Assign (tekst,'lpt1'); Rewrite (tekst); Writeln (tekst, t); Close (tekst) END. b) pliki zdefiniowane: Read (zmienna_plikowa, lista_zmiennych_we); gdzie zmienna_plikowa - plik logiczny skojarzony z zewnętrznym zbiorem danych (otwarty) lista_zmiennych_we - zmienne tego samego typu, jak typ elementów pliku, oddzielone przecinkami. PRZYKŁAD 5. Omówienie programu Przyklad5 (Pliki5.pas) PROGRAM Przyklad5; {czytanie plikow zdefiniowanych} USES Crt; CONST sciezka = 'c:\ksiazki.dan'; TYPE Ksiazka = RECORD numer: 1..1000; autor: String [30]; tytul: String [50] VAR katalog: FILE OF Ksiazka; dane: Ksiazka; Assign (katalog, sciezka); {skojarzenie zmiennej plikowej ze zbiorem dyskowym} Reset (katalog); {otwarcie pliku do odczytu} Read (katalog, dane); {czytanie rekordu dane z pliku} WITH dane DO {wydruk danych o ksiazce na ekranie monitora} Writeln; Writeln ('Numer: ',numer); Writeln ('Autor: ',autor); Writeln ('Tytul: ',tytul); Readln; ClrScr; Close (katalog) {zamkniecie pliku}
Programowanie strukturalne - pliki 11 END. c) pliki niezdefiniowane: BlockRead (zmienna_plikowa, bufor, licznik); lub BlockRead (zmienna_plikowa, bufor, licznik, wynik); gdzie bufor - dowolna zmienna licznik - wyrażenie typu Word, określające ilość wyprowadzanych zapisów wynik - zmienna typu Word, określająca ilość faktycznie wprowadzonych zapisów. PODSUMOWANIE zapis elementu do pliku odczyt elementu z pliku Plik tekstowy Plik zdefiniowany Plik niezdefiniowany Write Write BlockWrite Writeln Read Read BlockRead Readln Funkcje pomocnicze do obsługi plików b) pliki tekstowe EOF (zmienna_plikowa) przyjmuje wartość True, gdy 1. plik jest pusty lub 2. plik znajduje się przed ostatnim znakiem (Ctrl-Z), False w przeciwnym razie EOLN (zmienna_plikowa) przyjmuje wartość True, gdy 1. plik jest w pozycji końca wiersza lub 2. EOF dla tej zmiennej na wartość True False w przeciwnym razie SeekEOF (zmienna_plikowa) SeekEOLN (zmienna_plikowa - analogicznie do EOF, EOLN, ignorują najbliższe spacje i znaki tabulacji. c) pliki zdefiniowane i niezdefiniowane: Funkcje: EOF (zmienna_plikowa) przyjmuje wartość True przed ostatnim elementem lub gdy plik pusty, False - w przeciwnym razie FilePos (zmienna_plikowa): LongInt
Programowanie strukturalne - pliki 12 - zwraca aktualne położenie pliku określonego przez zmienną plikową. Przyjmuje 0, gdy jest to początek pliku. FileSize (zmienna_plikowa): LongInt; - zwraca rozmiar wyspecyfikowanego pliku, 0 gdy zbiór jest pusty. Procedury: Seek (zmienna_plikowa, pozycja) - procedura powodująca przesunięcie aktualnego położenia pliku do elementu o liczbie porządkowej podanej przez zmienną pozycja. PRZYKŁAD 6. Seek (katalog, 3) Elementy w pliku są numerowane od 0. Truncate (zmienna_plikowa) - procedura usuwająca z zewnętrznego zbioru danych wszystkie jego elementy, począwszy od aktualnej pozycji do końca zbioru. Inne operacje na plikach. ChDir (S: String) - procedura zmieniająca bieżący katalog na katalog określony przez zmienną S. Procedurą tą można również zmieniać napęd. PRZYKŁAD 7. ChDir ('c:'); GetDir (N: Byte; VAR Katalog: String) - procedura umieszczająca w zmiennej Katalog nazwę katalogu bieżącego w napędzie określonym zmienną N. Zmienna N przyjmuje następujące wartości: 0 napęd bieżący 1 napęd a 2 napęd b 3 napęd c itd. Procedura nie wykonuje kontroli błędów. Jeżeli napęd został błędnie określony w zmiennej N, to zmienna katalog będzie zawierała '\'. PRZYKŁAD 8. VAR katalog: String; GetDir (3, katalog); Writeln ('Bieżący katalog: ', katalog); MkDir (Nazwa: String)
Programowanie strukturalne - pliki 13 - procedura tworząca w katalogu bieżącym podkatalog o nazwie określonej parametrem Nazwa. PRZYKŁAD 9. MkDir ('DANE'); RmDir (NazwaKatalogu: String) - procedura usuwająca pusty katalog, którego nazwę wraz z ewentualną ścieżką dostępu określa parametr NazwaKatalogu. Erase (VAR f) - procedura usuwająca plik zewnętrzny skojarzony ze zmienną F (plik ten nie może być otwarty). PRZYKŁAD 10. VAR f: File; Assign (f, 'Tekst.txt'); Erase (f); Rename (VAR f; NowaNazwa: String); - procedura zmieniająca nazwę pliku dyskowego skojarzonego ze zmienną F, na nazwę określoną parametrem NowaNazwa. Nie można używać dla plików otwartych. PRZYKŁAD 11. VAR zbior: File; Assign (zbior, 'proba1'); Rename (zbior, 'Proba2'); IOResult: Word - funkcja zwracająca kod błędu powstałego podczas ostatniej operacji wejścia/wyjścia. Zastosowanie: {$I-} p:= IOResult; CASE p OF kod błędu: reakcja; {$I+}
Programowanie strukturalne - pliki 14 LEKCJA 31. OPERACJE NA PLIKACH. ŁĄCZENIE PLIKÓW Sortowanie plików sekwencyjnych (sortowanie zewnętrzne) Algorytmów sortowania dla tablic nie można, niestety, stosować wtedy, gdy dane przeznaczone do posortowania nie mieszczą się w pamięci operacyjnej komputera i są przechowywane w pamięci zewnętrznej (na przykład pliki). Zasadniczą cechą pliku jest to, że w każdej chwili dostępny jest bezpośrednio jeden i tylko jeden składnik; dlatego też trzeba stosować zupełnie odmienne metody sortowania. Jedną z najważniejszych metod jest sortowanie przez łączenie. Łączenie oznacza wiązanie dwóch (lub więcej) ciągów uporządkowanych w jeden ciąg uporządkowany przez wybieranie kolejnych obiektów spośród aktualnie dostępnych elementów tych ciągów. Łączenie jest o wiele prostsze od sortowania i jest stosowane jako operacja pomocnicza w bardziej złożonym procesie sortowania sekwencyjnego. Jednym ze sposobów sortowania opartym na łączeniu jest tzw. łączenie proste: 1. dzielimy ciąg a na połowy nazwane b i c. 2. łączymy ciągi b i c składając pojedyncze ich elementy w pary uporządkowane 3. nazywamy połączony ciąg a i powtarzamy kroki 1 i 2, łącząc tym razem pary uporządkowane w czwórki uporządkowane 4. powtarzamy poprzednie kroki łącząc czwórki w ósemki i dalej postępujemy w ten sam sposób, podwajając za każdym razem długość łączonych podciągów dopóty, dopóki cały ciąg nie zostanie uporządkowany. PRZYKŁAD 44 55 12 42 94 18 06 67 W 1. kroku otrzymamy: 44 55 12 42 94 18 06 67 Łączenie w pary uporządkowane: 44 94 ' 18 55 ' 06 12 ' 42 67 Po ponownym podzieleniu: 44 94 18 55 06 12 42 67 Łączenie w czwórki uporządkowane: 06 12 44 94 ' 18 42 55 67 Następny podział 06 12 44 94 18 42 55 67 Łączenie w ósemki uporządkowane: 06 12 18 42 44 55 67 94 Koniec. O sortowaniu: N. Wirth: Algorytmy + Struktury Danych = Programy, WNT 1989, rozdział 2 i 3. L. Banachowski. K. Diks, W. Rytter, WNT 1999, rozdziały 2.9 i 2.10.
Programowanie strukturalne - pliki 15 Łączenie plików uporządkowanych Algorytm scalania dwóch ciągów uporządkowanych: Dane: Dwa uporządkowane ciągi x i y. Wynik: Uporządkowany ciąg z będący scaleniem ciągów x i y. Krok 1: Dopóki oba ciągi x i y nie są puste, wykonuj następującą operację: przenieś mniejszy z najmniejszych elementów z ciągów x i y do ciągu z Krok 2: Do końca ciągu z dopisz elementy pozostałe w jednym z ciągów x lub y. ALGORYTM DOBIERANIA JEDNOPRZEBIEGOWEGO dla zadania łączenia plików posortowanych Założenia: 1. Zdefiniowano dwa typy danych: a) typ składowy: _T = RECORD k : _Klucz; dane : _Dane b) typ plikowy: _F = FILE OF _T; 2. WARTOWNIK jest nazwą stałej (albo zmiennej o ustalonej wartości) typu _Klucz, która: a) nie jest nigdy wartością klucza danej, b) jest późniejsza od wszystkich używanych wartości kluczy (w sensie porządku, przyjętego w typie _Klucz). 3. Zdefiniowano procedurę o nagłówku: PROCEDURE Czytaj(VAR F : _F; VAR e : _T), która: a) jeśli plik F jest niepusty, udostępnia element bieżący pliku F i zapisuje go do zmiennej e, b) jeżeli plik F jest pusty, to wynikowa wartość e jest następująca: pole klucza k przyjmuje wartość WARTOWNIK a pole dane jest nieokreślone Procedura ta może mieć postać : IF EOF(F) THEN e.k := Wartownik ELSE Read (F, e)
Programowanie strukturalne - pliki 16 Szkielet procedury łączenia plików posortowanych: PROCEDURE Lacz (VAR plik, nabytki, nowy_plik : _F); {łączymy plik z nabytki w jeden plik nowy_plik; pliki są już przygotowane do obsługi : plik i nabytki do odczytu, nowy_plik do zapisu. Znak porównania a<b oznacza ścisłe poprzedzanie elementu b przez element a} VAR stary, nowy : _T; Czytaj (plik, stary); Czytaj (nabytki, nowy); WHILE (stary.k < wartownik) OR (nowy.k < wartownik) DO {dopóki przynajmniej jeden z kluczy nie jest niewłaściwy} IF stary.k < nowy.k THEN Write (nowy_plik, stary); Czytaj (plik, stary) END {then} ELSE Write (nowy_plik, nowy); Czytaj (nabytki, nowy) END {else} {Lacz}
Programowanie strukturalne - pliki 17 SPIS TREŚCI Lekcja 29. Typy strukturalne - pliki.. 1 Lekcja 30. Przetwarzanie plików..6 Lekcja 31. Operacje na plikach. Łączenie plików.14