Podstawy Programowania
Wykład trzynasty:
Zarządzanie plikami (moduł DOS)
1. System plików
Jeśli program podczas swojego działania posługuje się więcej niż jednym pli-
kiem (np. tworzy pliki tymczasowe), to może się okazać konieczne wyposażenie
go w możliwość zarządzania tymi plikami, czyli możliwość współpracy z sys-
temem plików. Poprzez termin zarządzanie plikami będziemy rozumieć takie
czynności, jak: usuwanie plików, zmiana ich nazwy, zmiana atrybutów, uzy-
skiwanie informacji o plikach, uporządkowanie plików w hierarchię (budowanie
struktury katalogów). Ponieważ ilość miejsca na nośnikach danych jest
ograniczona pożądanym byłoby, gdyby program mógł dysponować informacjami
o niej. Twórcy Turbo Pascala wyposażyli tej język w podprogramy umożliwiające
wykonanie opisanych wyżej czynności. Część z nich jest umieszczona w module
System1, a część (wraz z innymi elementami) w module DOS2.
2. Proste operacje związane z zarządzaniem plikami
Przegląd możliwości Turbo Pascala pod względem zarządzania plikami za-
czniemy od operacji, które zostały zaimplementowane w postaci podprogramów
dostępnych w module System. Znajdują się tam dwie procedury, które umożli-
wiają manipulowanie plikami. Pierwszą z nich jest erase. Procedura ta przyj-
muje tylko jeden parametr. Jest nim zmienna plikowa, skojarzona z plikiem,
który ma zostać przez tę procedurę usunięty. Należy pamiętać, że ten plik nie
może być otwarty. Drugą ze wspomnianych procedur jest procedura rename,
zmieniająca nazwę pliku. Wymaga ona dwóch parametrów wywołania.
Pierwszym jest zmienna plikowa, drugim łańcuch znaków (lub zmienna typu
string) określająca nową nazwę pliku. Zmieniana jest nazwa pliku, który
stowarzyszony jest ze zmienną plikową. Turbo Pascal zawiera również
podprogramy służące do zarządzania katalogami3. Procedura mkdir pozwala
utworzyć katalog o podanej nazwie. Jako argument wywołania przyjmuje
zmienną lub ciąg znaków będący nazwą katalogu. Procedura rmdir pozwala
usunąć katalog o podanej nazwie, o ile nie jest on katalogiem bieżącym i jest
pusty (nie zawiera plików lub innych katalogów). Przyjmuje ona tylko jeden
parametr wywołania. Może on być zmienną typu string lub ciągiem znaków
określającym nazwę katalogu do usunięcia. Procedura chdir pozwala zmienić
katalog bieżący (tzn. ten w którym program bieżąco zapisuje lub odczytuje
1 Przypominam, że tego modułu nie trzeba włączać do program, kompilator robi to automatycz-
nie.
2 Środowisko Free Pascal również dysponuje takim modułem ze względów kompatybilności
z Turbo Pascalem. Różnice między wersjami tego modułu nie będą tu opisywane.
3 Katalog, określany niekiedy mianem folderu, jest plikiem, który zawiera informacje o innych pli-
kach. Katalogi mogą być organizowane w różne struktury hierarchiczne. Najczęściej spotykaną
taką strukturą jest drzewo.
2
pliki). Przyjmuje ona taki sam rodzaj parametru wywołania, jak poprzednio opi-
sywana procedura. Wynik działania wszystkich opisanych dotąd pod-
programów możemy poznać dzięki funkcji IOResult, której sposób działania
i użycia był opisywany na wykładzie o operacjach na plikach. Ostatnia z opi-
sywanych w tym rozdziale procedur nie ma tej własności. Tą procedurą jest
getdir, która zwraca nazwę bieżącego katalogu na określonym napędzie. Pierw-
szym argumentem wywołania tej procedury jest zmienna, wartość lub wyraże-
nie typu byte. Oznacza on numer napędu, którego katalog bieżący ma być
przez procedurę zwrócony. Jeśli ten argument ma wartość 0 (zero), to jest to
napęd bieżący, 1 (jeden), to jest to napęd A:, 2 napęd B:, 3 napęd C, itd.
Drugim argumentem wywołania tej procedury jest zmienna typu string, do
której będzie zapisana nazwa tego katalogu.
3. Elementy modułu DOS związane z systemem plików
Jak zostało to napisane we wstępie część elementów modułu DOS związana jest
z zarządzaniem plikami. Do tych elementów należy zmienna DosError, typu
integer, do której większość podprogramów zgromadzonych w tym module
zapisuje kod wykonania. Jeśli po wykonaniu takiego podprogramu zmienna ta
ma wartość 0 (zero), to oznacza, że wykonanie tego podprogramu zakończyło się
sukcesem. Pozostałe możliwości to: 2 plik nie został znaleziony, 3 ścieżka
nie została znaleziona, 5 odmowa dostępu, 6 niewłaściwy identyfikator4 pli-
ku, 8 brak pamięci (komunikat nie związany z obsługą plików), 10 zła war-
tość zmiennej środowiskowej (jak wyżej), 11 niewłaściwy format, 18 nie ma
więcej plików. Z każdym plikiem związany jest zbiór cech nazywany atrybuta-
mi. Możemy je poznać dzięki procedurze getfattr, która przyjmuje dwa parame-
try wywołania. Pierwszym jest zmienna plikowa, która jest skojarzona z pli-
kiem, którego atrybuty chcemy poznać, drugim jest zmienna typu word, do
której zapisywane są wartości atrybutów. Pojedynczy atrybut jest reprezen-
towany przez jeden bit tej zmiennej lub grupę bitów. Przykładowo, jeśli usta-
wiony jest najmłodszy bit tej zmiennej, to plik jest tylko do odczytu. Wartość
poszczególnych bitów możemy poznać dzięki operacji maskowania
wykonywanej z użyciem operatora and. Operator ten wymaga dwóch
argumentów. Tym drugim argumentem mogą być predefiniowane stałe
z modułu DOS: ReadOnly ($01) plik tylko do odczytu, Hidden ($02) plik
ukryty, SysFile ($04) plik systemowy, VolumeID ($08) etykieta, Directory
($10) katalog, Archive ($20) plik archiwalny, AnyFile ($3F) zwykły plik5.
Plik, którego atrybuty badamy nie może być otwarty. Procedura getfattr nie in-
4 Bardziej powszechnym tłumaczeniem jest uchwyt zamiast identyfikator .
5 Stałe te nazywane są maskami. Technika maskowania jest używana nie tylko w odniesieniu do
plików.
3
formuje o tym, czy została wykonana poprawnie. Do ustawienia atrybutów pli-
ku służy procedura setfattr. Przyjmuje ona takie same argumenty wywołania
jak getfattr, ale drugi argument niekoniecznie musi być zmienną, może być
wyrażeniem lub wartością6 typu word. Ponownie, plik którego atrybuty
zmieniamy nie może być otwarty. Procedura getftime służy do uzyskania
informacji o czasie ostatniej modyfikacji (zapisie) pliku. Pobiera ona dwa
parametry. Pierwszym jest zmienna plikowa skojarzona z plikiem, który jest
otwarty. Drugim jest zmienna typu longint. Procedura nie sygnalizuje
poprawności swojego wykonania. Do ustawienia czasu ostatniej modyfikacji
pliku służy procedura setftime. Przyjmuje ona takie same argumenty, jak
poprzednio opisywana, ale drugi argument może być wyrażeniem lub wartością
typu longint, niekoniecznie zmienną. Czas, a właściwie data i czas jest
podawany w postaci skompresowanej wartości typu longint. Żeby
zdekompresować tę informację lub żeby ją skompresować możemy użyć
procedur packtime i unpacktime. Pierwsza z nich dokonuje kompresji. Przyj-
muje ona dwa argumenty wywołania. Pierwszym jest rekord typu DateTime,
a drugim zmienna typu longint, do której zapisywana jest skompresowana
informacja. Typ rekordowy DateTime jest zdefiniowany w module DOS
następująco:
type
DateTime = record
Year,Month,Day,Hour,Min,Sec:Word;
end;
Procedura unpacktime działa w drugą stronę dokonuje dekompresji. Podobnie
jak packtime przyjmuje dwa argumenty wywołania, ale pierwszym jest zmienna,
wyrażenie lub wartość typu longint, a drugim rekord typu datetime, do którego
zostanie zapisana zdekompresowana informacja. Obie procedury nie informują
o wyniku swojego wykonania. Funkcje diskfree i disksize zwracają odpowiednio
ilość wolnej przestrzeni na nośniku i pojemność nośnika w bajtach. Wartość,
którą zwracają jest typu longint. Jako argument wywołania przyjmują wartość
typu byte określającą numer napędu, tak jak zostało to przedstawione w opisie
procedury getdir. Jeśli argument wywołania jest błędny, to funkcje zwracają
wartość -1. Ponieważ zostały one stworzone w czasach, gdy pojemności pamięci
masowych były niewielkie, mogą dawać błędne wyniki i nie należy im wierzyć.
Do wyszukania wystąpienia plików o podanych nazwach służą procedury
findfirst i findnext. Pierwszym argumentem wywołania procedury findfirst jest
ciąg znaków7 określający nazwę pliku, którego wystąpienie w katalogu chcemy
6 W szczególności może to być jedna z opisanych stałych, lub wyrażenia zbudowane z użyciem
tych stałych i operatorów or.
7 Właściwie ciąg znaków zakończony znakiem o kodzie ASCII równym zero (ASCIIZ). Istnieje spe-
cjalny typ danych pozwalający na zdefiniowanie zmiennej przechowującej takie łańcuchy
4
stwierdzić. Ten ciąg niekoniecznie musi być wprost nazwą. Może on również
zawierać znaki globalne, które pozwalają konstruować tzw. wyrażenia
regularne. Tymi znakami są * i ? . Znak gwiazdki (ang. asterisk) oznacza ciąg
dowolnych znaków o dowolnej długości8. Znak pytajnika oznacza pojedynczy,
ale dowolny znak. Za ich pomocą możemy np. wyszukać wszystkie pliki
z rozszerzeniem txt (*.txt) lub pliki tekstowe, których nazwa, oprócz ostatniego
znaku zawiera wyrażenie list (list?.txt). W szczególności możemy określić na-
zwę dowolnego pliku za pomocą wyrażenia regularnego *.* . Opisywany ciąg
znaków może być również pełną ścieżką dostępu, tzn. zawierać symbol napędu
i nazwy katalogów, np. 'c:\tp\bin\*.*'. Jeśli stosujemy znaki globalne w nazwie
pliku, to zapewne będziemy chcieć znalezć więcej niż tylko jeden plik, którego
nazwa pasuje do wzorca. Jeśli wywołanie findfirst zwróci pozytywny wynik, to
możemy wywołać findnext, aby znalezć pozostałe poszukiwane pliki, o ile one
istnieją. Drugim argumentem wywołania przyjmowanym przez procedurę
findfirst jest zmienna, wyrażenie lub wartość typu word, która opisuje atrybuty
poszukiwanego pliku. Ostatni parametr wywołania musi być zmienną i musi to
być rekord typu SearchRec. Typ ten jest zdefiniowany w module DOS
następująco:
SearchRec = record
Fill:array[1..21] of Byte;
Attr:Byte;
Time:Longint;
Size:Longint;
Name:string[12];
end;
W tym rekordzie są zwracane informacje o pliku, które są zawarte w katalogu,
do którego ten plik należy. Pole Fill jest tablicą o dwudziestu jeden elementach
typu byte. Po wywołaniu findfirst lub findnext będzie ono zawierała informacje,
które można pominąć9. Drugie pole jest typu byte i będzie zawierało atrybuty
pliku10. Pole Time będzie zawierało spakowany czas i datę ostatniej modyfikacji
pliku, pole Size będzie zawierało rozmiar pliku wyrażony w bajtach. Pole Name
będzie zawierało łańcuch dwunastu znaków, które będą stanowić nazwę pliku
(osiem znaków - nazwa właściwa, kropka i trzy znaki rozszerzenia). Procedura
findnext przyjmuje tylko jeden parametr wywołania rekord typu SearchRec.
Funkcja FSearch służy do przeszukiwania wybranych katalogów, które mogą
znaków, ale nie będzie on tutaj opisywany.
8 Również zerowej.
9 Są to informacje zastrzeżone wyłącznie dla systemu DOS.
10 Proszę zwrócić uwagę na różnicę w stosunku do getfattr i setfattr, dla których atrybuty były
reprezentowane przez wartość typu word.
5
zawierać określony przez nas plik. Pierwszym argumentem tej funkcji jest
zmienna lub łańcuch znaków typu PathStr. Typ PathStr jest typem zdefiniowa-
nym w module DOS i jest to typ string, którego pojemność została ograniczona
do 79 znaków. Pierwszy argument wywołania funkcji FSearch zawiera nazwę
zbioru, który chcemy wyszukać. Drugi parametr wywołania jest zmienną lub
łańcuchem znaków typu string i określa listę katalogów, które funkcja powinna
przeszukać. Jeśli ten łańcuch zawiera więcej niż jedną ścieżkę do katalogu, to
należy je rozdzielić średnikami. Jeśli podając ścieżkę nie podamy nazwy na-
pędu, to funkcja przyjmie, że ta ścieżka dotyczy bieżącego napędu. Jeśli
łańcuch będzie pusty, to przeszukany zostanie bieżący katalog w bieżącym na-
pędzie. Wartość zwracana przez tę funkcję jest typu PathStr. Jeśli jest nią ła-
ńcuch pusty, to oznacza, że plik o podanej nazwie nie występuje w podanych
katalogach. Jeśli plik występuje to jest podana pełna ścieżka dostępu do tego
pliku (wraz z nazwą pliku i napędu). Jeśli napęd i katalog, w którym znajduje
się plik są bieżące, to nie są one umieszczane w łańcuchu wynikowym. Funkcja
FExpand zwraca pełna nazwę pliku (wraz z oznaczeniem napędu i katalogami).
Jednym parametrem wejściowym, jaki ta funkcja przyjmuje jest łańcuch
znaków lub zmienna typu PathStr. Wartość zwracana przez funkcję jest również
tego typu. Argument jest oczywiście nazwą pliku, a wartość zwracana pełną
ścieżką dostępu do pliku11. Przykładowa pełna ścieżka do pliku może być na-
stępująca 'c:\tp\bin\turbo.exe', gdzie 'c:\' oznacza katalog główny (znak lewego
ukośnika nazywanego po angielsku backslash oznacza w tym wypadku kata-
log główny), turbo.exe jest nazwą pliku, a pozostałe znaki nazwami katalogów,
rozdzielone lewymi ukośnikami. Zapis 'tp\bin' oznacza, ze katalog bin jest pod-
katalogiem katalogu tp (inaczej: zawiera się w nim). Pełną ścieżkę do pliku
możemy rozbić na poszczególne składowe przy pomocy procedury FSplit.
Pierwszym argumentem wywołania tej procedury jest zmienna typu PathStr lub
łańcuch znaków będący pełną ścieżką dostępu do pliku. Pozostałymi
argumentami wywołania są: zmienna typu DirStr (string o pojemności
ograniczonej do 67 znaków), która będzie zawierała nazwę katalogu (lub ciąg
nazw katalogów) wyciętą z podanej w pierwszym parametrze ścieżki dostępu,
zmienna typu NameStr (string o pojemności ograniczonej do ośmiu znaków),
która będzie zawierała nazwę właściwą pliku i zmienna typu ExtStr (string
o pojemności ograniczonej do czterech znaków), która będzie zawierała kropkę
i trzy znaki rozszerzenia pliku.
4. Przykłady
Poniżej zostanie zaprezentowanych kilka przykładowych programów wykorzy-
stujących opisane wyżej podprogramy w praktyce. Pierwszy przykład będzie wy-
pisywał na ekranie nazwy wszystkich plików w bieżącym katalogu, ich atry-
11 Pełna ścieżka dostępu określana jest w literaturze pełną nazwą pliku .
6
buty, rozmiar oraz datę i czas ostatniej modyfikacji.
program list_files;
uses
crt,dos;
function decode(a:word):string;
var
tmp:string;
begin
tmp:='';
if a and readonly = readonly then tmp:=tmp+'r' else tmp:=tmp+'-';
if a and hidden = hidden then tmp:=tmp+'h' else tmp:=tmp+'-';
if a and sysfile = sysfile then tmp:=tmp+'s' else tmp:=tmp+'-';
if a and volumeid = volumeid then tmp:=tmp+'v' else tmp:=tmp+'-';
if a and directory = directory then tmp:=tmp+'d' else tmp:=tmp+'-';
if a and archive = archive then tmp:=tmp+'a' else tmp:=tmp+'-';
decode:=tmp;
end;
procedure print(const sr:searchrec);
var
f:file;
atw:word;
dt:datetime;
begin
assign(f,sr.name);
getfattr(f,atw);
unpacktime(sr.time,dt);
writeln(fexpand(sr.name),' ',decode(atw),' ',dt.year,'-',dt.month,'-',dt.day,'
',dt.hour,':',dt.min,':',dt.sec,' ',sr.size);
end;
procedure find(const dir:string);
function changedir(const x:string):byte;
begin
7
{$I-}chdir(x);{$I+}
if ioresult<>0 then
begin
writeln('Zmiana katalogu nie powiodła się.');
changedir:=1;
end
else changedir:=0;
end;
var
sr:searchrec;
str:string;
liczba_wierszy:0..maxlongint;
begin
liczba_wierszy:=0;
getdir(0,str);
if changedir(dir)<>0 then exit;
findfirst('*.*',anyfile,sr);
if doserror=0 then
begin
print(sr);
inc(liczba_wierszy);
repeat
findnext(sr);
if doserror=0 then print(sr);
inc(liczba_wierszy);
while keypressed do readkey;
if liczba_wierszy mod 24 = 0 then readkey;
until doserror<>0;
end
else
writeln('Brak plików w podanym katalogu.');
changedir(str);
end;
8
begin
clrscr;
find('.');
readln;
end.
W programie została zdefiniowana procedura find, która wypisuje informacje
o wszystkich plikach z katalogu, którego nazwa została jej przekazana przez
parametr. Pierwszą czynnością jaka jest wykonywana w tej procedurze jest za-
pamiętanie w zmiennej str nazwy bieżącego katalogu za pomocą procedury
getdir. Zmiana katalogu, na ten, którego nazwa została podana przez parametr
następuje za pomocą funkcji changedir. Ta funkcja została zdefiniowana jako
podfunkcja procedury search. Jeśli zmiana katalogu się powiodła zwraca ona
zero, jeśli nie jeden. Należy zauważyć, że w funkcji changedir korzystamy
z procedury chdir i funkcji ioresult do określenia wyniku operacji zmiany
katalogu. Do wyszukania plików używamy procedur findfirst i findnext.
Argumenty, które przekazujemy pierwszej z tych procedur oznaczają, że
chcemy wyszukać plik o dowolnej nazwie i dowolnych atrybutach. Jeśli
findfirst znajdzie choćby jeden plik, to procedura find wypisuje jego dane,
a następnie wykonuje pętlę repeat ... until, w której wywoływana jest procedura
findnext. Pętla ta kończy się kiedy zmienna doserror, modyfikowana przez tę
procedurę będzie miała wartość różną od zera. Wypisaniem danych o pliku
zajmuje się procedura print, która zostanie omówiona pózniej. Pełna informacja
o pliku jest wypisywana w jednym wierszu. W pętli liczona jest liczba
wypisanych wierszy (zmienna liczba_wierszy). Jeśli jest ona podzielna przez 24
(liczba wierszy na ekranie minus jeden), to program zatrzymuje się i czeka na
naciśnięcie przez użytkownika klawisza. Po zakończeniu pętli repeat procedura
zmienia katalog na ten, w którym pracował program przed jej wywołaniem.
Procedura print ma jeden parametr formalny, który jest rekordem typu
SearchRec. Atrybuty pliku nie są odczytywane z tego rekordu, lecz za pomocą
procedury getfattr. Proszę zwrócić uwagę, że zmienna plikowa jest typu file
i oznacza plik amorficzny. Może ona być również zmienną plikową innego typu,
gdyż nie będziemy otwierać pliku z nią skojarzonego. Czas i data ostatniej
modyfikacji pliku są dekompresowane za pomocą procedury unpacktime.
Wszystkie dane o pliku są wypisywane w ostatnim wierszy procedury print (nie
licząc wiersza ze słowem kluczowym end). Wypisywana jest pełna ścieżka
dostępu do pliku. Atrybuty pliku są zamieniane na łańcuch znaków za pomocą
funkcji decode. Jeśli plik posiada jakiś atrybut, to jest do łańcucha dodawany
znak reprezentujący go (np. r dla read only ), jeśli nie to jest dodawany
myślnik ( - ). W funkcji użyto maskowania do uzyskania informacji
9
o poszczególnych atrybutach pliku. W bloku programu głównego procedura
find jest wywoływana z parametrem '.'. Kropka oznacza nazwę katalogu
bieżącego. Dwie kropki ('..') oznaczają nazwę katalogu nadrzędnego,
w stosunku do katalogu bieżącego. Każdy katalog (poza katalogiem głównym
napędu) zawiera takie nazwy. Zamiast '.' w wywołaniu procedury find można
użyć nazwy dowolnego istniejącego katalogu.
program del_files;
uses
crt,dos;
procedure del(const sr:searchrec);
var
f:file;
cz:char;
begin
writeln('Skasować ',fexpand(sr.name),' (t/n)?');
cz:=upcase(readkey);
if cz='T' then
begin
assign(f,sr.name);
{$I-}erase(f);{$I+}
if ioresult<>0 then writeln('Usuwanie pliku nie powiodło się.');
end;
end;
procedure find(const dir:string);
function changedir(const x:string):byte;
begin
{$I-}chdir(x);{$I+}
if ioresult<>0 then
begin
writeln('Zmiana katalogu nie powiodła sie.');
changedir:=1;
end
10
else changedir:=0;
end;
var
sr:searchrec;
str:string;
begin
getdir(0,str);
if changedir(dir)<>0 then
begin
readln;
exit;
end;
findfirst('*.bak',anyfile,sr);
if doserror=0 then
begin
del(sr);
repeat
findnext(sr);
if doserror=0 then del(sr);
until doserror<>0;
end else
writeln('Brak szukanych plików w podanym katalogu.');
changedir(str);
end;
begin
clrscr;
find('.');
readln;
end.
Powyższy program jest podobny do poprzedniego. Niewielkim zmianom uległa
definicja procedury find. Tym razem jej zadaniem jest wyszukanie i usunięcie
wszystkich plików z rozszerzeniem bak . Usunięcie dokonywane jest za
11
pomocą procedury del, która najpierw pyta użytkownika, czy skasować
znaleziony plik. Jeśli odpowiedz jest twierdząca (tzn. użytkownik wprowadził
znak t lub T ), to zmienna plikowa jest kojarzona z nazwą pliku, a następnie
przekazywana do wywołania procedury erase. Wynik działania tej procedury
jest badany za pomocą wywołania funkcji ioresult. Jeśli usunięcie pliku nie
powiodło się, to wypisywany jest na ekran odpowiedni komunikat.
12
Wyszukiwarka
Podobne podstrony:
PP1 wykladPP1 wyklad 8PP1 wyklad 3PP1 wyklad 5PP1 wyklad 1PP1 wyklad 2PP1 wykladPP1 wyklad 4PP1 wyklad 5PP1 wyklad 9PP1 wyklad 4PP1 wykladSieci komputerowe wyklady dr FurtakWykład 05 Opadanie i fluidyzacjaWYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznejwięcej podobnych podstron