Pliki przechowywane na dysku twardym mogą być formatu tekstowego lub binarnego. Gdy otworzysz plik tekstowy, można przeczytać konkretną wiadomość. Gdy natomiast otworzysz w edytorze tekstu plik binarny zobaczysz wiele nic nie mówiących Tobie znaków. Dla komputera mają one jednak znaczenie.
W plikach przechowujemy dane, które chcemy zatrzymać po wyłączeniu programu. Mogą w nich też znajdować się potrzebne informacje do działania naszego programu. Np. dane personalne, mapy do gry, zdjęcia, muzyka, lista najlepszych wyników.
Obsługa plików typu tekstowego jest bardzo prosta. Prawie nie różni się od zwyczajnego używania procedur Write, WriteLn, Read, ReadLn. Najpierw jednak potrzebujemy otworzyć konkretny plik i zapamiętać jego uchwyt (miejsce przez które będziemy się z nim łączyli). Potem używamy odpowiednich procedur (Read, Write). Na koniec zamykamy plik. I to wszystko!
Otwieranie plików
Kojarzenie plików odbywa się przed ich otwarciem, za pomocą procedury Assign(var F:File; FileName:string); Dzięki kojarzeniu przypisujemy pewnej zmiennej uchwyt, czyli miejsce w pamięci, które będzie identyfikować nasz plik.
W przypadku, gdy pracujemy na pliku tekstowym, uchwyt zmiennej będzie typu TEXT. W innych przypadkach może być FILE, albo FILE OF {…} nazwa jakiegoś rekordu własnego typu.
Otwieranie do odczytu
Aby otworzyć plik do odczytu skorzystaj z procedury Reset(var F:FILE; [RecSize:Word]). Zmienna pierwsza typu FILE jest właśnie uchwytem do pliku, druga oznacza rozmiar odczytywanego rekordu, domyślnie 128.
Otwieranie do zapisu
Do zapisu pliku służy procedura Rewrite(var F:FILE; [RecSize:Word]). Jej parametry skonstruowane podobnie jak Reset.
Przykłady użycia
{naglowek i potrzebne zmienne}
program zapis_i_odczyt;
var F:Text;
s:string;
begin
{najpierw zapis do pliku}
Assign(F, 'plik.txt');
Rewrite(F);
WriteLn(F, 'Ala ma kota');
Close(F);
{teraz odczyt z pliku}
Assign(F, 'plik.txt');
Reset(F);
ReadLn(F, s);
Close(F);
WriteLn(s);
end.
Z plików tekstowych można odczytywać również liczby. Jeśli ma być ich wiele, muszą być oddzielone pojedynczymi spacjami. Wtedy podobnie jak wczytujemy z klawiatury, możemy użyć wczytywania z plików,
np. odczytanie trzech liczb z pliku, którego zawartość to:
1 2 3
wykonujemy za pomocą
ReadLn(F, a, b, c);
Gdy w następnej lini jest kilka innych liczb, niekoniecznie tyle samo, można wczytać je w kolejnej instrukcji ReadLn. Jednak programista musi czuwać nad tym, by nie wowołać pustego wczytywania, czyli takiego gdy plik się już skończył.
Uwaga!
Nie jest dobrze traktować plików binarnych (np. bitmapy) jako tekstowych i odczytywać ich zawartość po jednym znaku typu char (ani kopiować plików w ten sposób). Oczywiście całość zadziała i można wyświetlić taki rysunek lub skopiować plik, jednak wczytywanie z pliku bajt po bajcie (również zapisywanie) jest bardzo czasochłonne. Zwykła bitmapa może być wczytywana nawet kilka sekund! Za pomocą instrukcji BlockRead i BlockWrite można to zrobić w granicach kilkunastu milisekund.
Wczytywanie i zapisywanie w plikach binarnych odbywa się za pomocą instrukcji BlockRead, BlockWrite. Jest to bardzo szybka metoda zapisu i odczytu. Najlepiej zobaczyć prosty przykład w pomocy pascala, który kopiuje plik podany jako parametr programu na miejsce podane drugim parametrem
{Blockrd.PAS}
{Sample code for the BlockRead and BlockWrite procedures.}
program CopyFile;
{ Simple, fast file copy program with NO error-checking }
{ For Windows: }
{ uses WinCrt; }
var
FromF, ToF: file; {uchwyty do plikow}
NumRead, NumWritten: Word;
Buf: array[1..2048] of Char; {bufor, do ktorego beda trafialy wczytane dane z pliku pierwszego}
begin
Assign(FromF, ParamStr(1)); { Otwiera plik podany przez pierwszy parametr }
Reset(FromF, 1);
Assign(ToF, ParamStr(2)); { Przygotowuje do zapisu drugi plik }
Rewrite(ToF, 1); { Record size = 1 }
Writeln('Copying ', FileSize(FromF), ' bytes...');
repeat
BlockRead(FromF, Buf, SizeOf(Buf), NumRead); {odczytywanie bloku o rozmiarze 2KB (NumRead teraz okresla rozmiar)}
BlockWrite(ToF, Buf, NumRead, NumWritten); {zapisywanie odczytanego bloku}
until (NumRead = 0) or (NumWritten <> NumRead); {kopiowanie do momentu skopiowania calego pliku lub gdy wystapi blad}
Close(FromF); {Zamykanie plikow}
Close(ToF);
end.
Ten program nie zadziała tak od razu. Trzeba podać mu 2 parametry, które będą oznaczały 2 pliki. Pierwszym musi być plik, który rzeczywiście istnieje na dysku. Drugi parametr to plik tworzony. Jak wpisać te parametry? W menu Turbo Pascala wybierz Run->Parameters I wpisz np. C:plik.txt C:Kopia.txt.
Teraz utwórz na dysku C: plik o nazwie plik.txt i wpisz do niego jakąś zawartość. Gdy uruchomisz program, zobaczysz, że na dysku powstał drugi plik o nazwie Kopia.txt, w dodatku z taką samą zawartością jak plik.txt.
Warto zwrócić uwagę na pewną funkcję w podanym przykładzie. Chodzi o FileSize. Funkcja FileSize zwraca rozmiar pliku, którego kojarzymy ze zmienną typu File.
Podczas pracy z plikami, może się zdarzyć, że kopiowany plik nie istnieje albo że plik, do którego chcemy coś dopisać został zabezpieczony, lub używa go inny program. Co wtedy? Gdy nie dodamy zabezpieczeń, nasz program zostanie przerwany.
Pascal udostępnia dyrektywy kompilatora, które pozwalają na przejęcie kontroli nad pojawiającymi się błędami. Taką dyrektywą jest {$I-} oraz {$I+} Gdy pomiędzy nimi umieścimy kod, będzie on odporny na błędy wejścia/wyjścia, inaczej mówiąc odporny również na błędy dotyczące obsługi plików.
A jak sprawdzić czy plik istnieje? Można napisać własną funkcję, np. FileExists. Będzie próbowała otworzyć plik. Jeśli się nie uda, będzie zwracała wartość False, czyli plik nie istnieje.
function FileExists(const FileName : string) : Boolean;
var F : File;
begin
{$I-}
Assign(F, FileName);
Reset(F, 1);
Close(F);
{$I+}
FileExists := IOResult = 0 ;
end;