1.10.2002
|
čo sa budeme dnes učiť
- nový jednoduchý typ char
a nový komplexnejší typ textový
súbor
- ukážeme, ako sa pracuje s textovým
súborom, aké tu platia pravidlá,
ako sa riešia jednoduché úlohy
- ukážeme zopár úloh,
v ktorých sa pracuje s korytnačkami a
s textovými súbormi
Typ CHAR
- znakové funkcie: pred, succ, ord, char
(v pascale funguje aj chr)
- treba si pamätať:
- ord(' ')=32; ord('0')=48; ord('1')=49; ...
alebo
#32=' '; #48='0'; #49='1'; ...
- ord('A')=65; ord('a')=ord('A')+32=97; ...
t.j.
#65='A'; #97='a'; ...
- ak c obsahuje znak cifry ('0'..'9'), tak ord(c)-ord('0')=cifra
(podobne pre písmená)
- príkazy inc, dec fungujú aj pre
znaky (napr. c:='A'; inc(c,32); )
- typ char využijeme najmä pri práci so súbormi
- so všetkými ordinálnymi typmi
(integer, boolean aj char) sa dá robiť for-cyklus
príklad for-cyklu pre rôzne
typy:
|
var
i,x:integer;
b:boolean;
z:char;
begin
for i:=1 to 100 do ... cyklus prejde 100-krát ...
for b:=false to true do ... cyklus prejde 2-krát ...
for b:=x=1 to x=2 do ... cyklus prejde 0, 1 alebo 2-krát ...
for z:='A' to 'Z' do ... cyklus prejde 26-krát ...
for z:=pred('#') to succ('#') do ... cyklus prejde 3-krát ...
|
Textová plocha
- aby sme mohli experimentovať so znakmi, s textovou
informáciou, naučíme sa v Delphi pracovať
s textovou plochou
- do prázdneho formulára položíme
komponent Memo (trieda TMemo) - podobne ako grafickú
plochu Image budeme ťahať aj túto plochu
- vytvorí sa komponent s menom Memo1 (v ľavom
hornom riadku sa objaví text Memo1)
- zatiaľ sa naučíme len niekoľko príkazov
na prácu s textovou plochou a rozumieť im
budeme až neskôr. Podobne ako sme písali
Image1.Canvas. a za tým príkaz, budeme
teraz písať Memo1.Lines. a za to príkaz:
- Memo1.Lines.Clear;
- vyčistí všetky riadky
- Memo1.Lines.Add('nejaký
text'); - pridá ďalší
riadok s daným textom na koniec
ukážka časti
programu s jedným komponentom Memo1, ktorý
vypisuje kódy písmen:
|
procedure TForm1.FormCreate(Sender: TObject);
var
z:char;
begin
Memo1.Lines.Clear;
for z:='a' to 'z' do
Memo1.Lines.Add(z+' '+IntToStr(ord(z)));
// k písmenu prilepí medzeru a číselný kód znaku
end;
|
Textové súbory TEXT
Súbor = postupnosť (sekvencia) prvkov rovnakého
typu, zaujímavé sú postupnosti
znakov, t.j. textové súbory + špeciálne
znaky <Eoln>. Všetky údaje
sú v textovom súbore zapísané
ako ASCII znaky.
- pascal umožňuje sekvenčný prístup do
súboru ale aj priamy (uvidíme neskôr)
- s textovými súbormi sa dá pracovať
len sekvenčným prístupom
- musí sa rozlišovať, či zo súboru čítame
alebo do neho zapisujeme údaje:
- vstupný súbor - len čítať pripravené
údaje
- výstupný súbor - len zapisovať,
na koniec súboru!
- do textového súboru sa nedá
aj zapisovať aj súčasne z neho čítať
Súboru (file) musí byť priradené
nejaké fyzické zariadenie, resp. musí
sa nachádzať na nejakom zariadení:
- na vonkajšej pamäti - diskový súbor
- pomocná vnútorná pamäť
- naučíme sa neskôr
"Operácie" so súbormi:
- write = zápis informácie na koniec
súboru (podobne ako do textovej ploche pomocou
Memo1.Lines.Add(...))
- read = čítanie nasledujúcej hodnoty
zo súboru
Ako pracujeme s textovým súborom
- deklarovanie premennej typu textový súbor:
var t:TextFile; // premenná t je typu textový
súbor
- priradenie fyzického súboru najčastejšie
na disku:
AssignFile(t,'meno_súboru');
- dávajte si pozor na uvádzanie plnej
cesty na disku k nejakému súboru - ak
sa takýto program prenesie na iný počítač,
s veľkou pravdepodobnosťou tieto absolútne cesty
k súborom nebudú fungovať - používajte
radšej relatívne cesty od momentálnej
adresy projektu (tam, kde sa nachádza EXE súbor)
- otvorenie súboru
- buď na čítanie (musí už existovať):
Reset(t);
- alebo zápis (ak už existuje, tak sa najprv
vyprázdni):
Rewrite(t);
- práca so súborom, t.j. samotné
čítanie alebo zapisovanie:
read(t,...); readln(t,...); write(t,...); writeln(t,...);
- zatvorenie súboru, t.j. ukončenie práce
so súborom:
CloseFile(t);
- textový súbor = postupnosť riadkov
(aj prázdna)
- pričom riadok textového súboru = postupnosť
znakov (aj prázdna) ukončená <Eoln>
Ak máme nejaký súbor:
vidíme:
|
v skutočnosti:
|
ab cde f
|
|a|b|<Eoln>| |c|d|e|<Eoln>|f| ^ ukazovateľ
|
- ukazovateľ = pozícia v súbore - na
začiatku je na 1. znaku; po read sa posunie o 1 znak
vpravo
Čítanie zo súboru
- príkazom read(t, premenná
) sa prečíta
jeden znak zo súboru z pozície ukazovateľa,
priradí sa jeho hodnota do premennej a ukazovateľ
sa posunie
Testovanie konca súboru a konca riadka
- štandardná logická funkcia Eof(t) =
skratka z End Of File
- vráti true, ak je ukazovateľ nastavený za
posledným znakom
súboru
- štandardná logická funkcia Eoln(t)
= skratka z End Of LiNe
- vráti true, ak je ukazovateľ na značke <Eoln>
alebo aj za posledným znakom súboru, t.j.
ak platí eof(t)=true, tak platí
eoln(t)=true
- značka <Eoln> sa vnútorne kóduje
dvomi znakmi #13 a #10 (hovoríme im CR a LF)
- príkazom readln(t) preskočíme všetky
znaky v súbore až za najbližšiu značku <Eoln>
(na konci súboru nerobí nič)
- POZOR, pomocou read(t,z) je možné čítať
aj značku <Eoln>, lenže táto sa potom chápe
ako 2 znaky #13 a #10 a nie ako nejaký špeciálny
znak
- príkaz readln(t,z) je skrátený
tvar pre read(t,z); readln(t);
- čítanie na konci súboru vyvolá
vstupno-výstupnú chybu
- znak #26 má v pascale (z historických
dôvodov) niekedy špeciálny význam:
čítanie textového súboru si
na ňom "myslí", že je na konci
súboru (eof(t)=true) a nedovolí ho
prečítať a ani čítať ďalšie znaky
za ním...
- môžete to otestovať tak, že si vytvoríte
textový súbor, ktorý bude
niekde v strede obsahovať znak #26, potom tento
súbor vypíšte pomocou Memo1.Lines.LoadFromFile(...)
a tiež ho čítajte a vypisujte pomocou
while not eof(t) do begin read(t,z); ...end;
V nasledujúcom príklade zistíme
počet medzier v textovom súbore medzery.txt:
|
var
t:TextFile;
z:char;
pocet:integer;
begin
AssignFile(t,'medzery.txt'); Reset(t);
pocet:=0;
while not Eof(t) do begin
read(t,z);
if z=' ' then inc(pocet);
end;
CloseFile(t);
Memo1.Lines.Add('Počet medzier v súbore '+IntToStr(pocet));
// do textovej plochy
end;
|
Príklad: Zistíme, počet riadkov textového
súboru text.txt:
|
var
t:TextFile;
pocet:integer;
begin
AssignFile(t,'text.txt'); Reset(t);
pocet:=0;
while not Eof(t) do begin
readln(t); inc(pocet);
end;
CloseFile(t);
Memo1.Lines.Add('Počet riadkov v súbore '+IntToStr(pocet));
end;
|
Príklad: Zistíme, dĺžku najdlhšieho
riadka súboru text.txt
|
var
t:TextFile;
z:char;
max,dlzka:integer;
begin
AssignFile(t,'text.txt'); Reset(t);
max:=0;
while not Eof(t) do begin
dlzka:=0;
while not Eoln(t) do begin
read(t,z); inc(dlzka);
end;
readln(t); // nesmie sa tu zabudnúť
if dlzka>max then max:=dlzka;
end;
CloseFile(t);
Memo1.Lines.Add('Dĺžka najdlhšieho riadka '+IntToStr(max));
end;
|
Zápis do súboru
- súbor treba otvoriť pomocou Rewrite(t)
- ak súbor už existoval, tak hneď po
Rewrite sa zruší jeho obsah
- ak súbor ešte neexistoval, vytvorí
sa s prázdnym obsahom
- ukazovateľ v súbore je vždy nastavený
na jeho koniec
- príkaz write(t, znak ) zapíše do
súboru jeden znak; write(t, reťazec
) zapíše
kompletný reťazec
- príkaz writeln(t) zapíše do súboru
značku <Eoln>, t.j. robí to isté
ako write(t,#13#10)
- príkaz writeln(t, reťazec ) je
skrátený tvar pre write(t, reťazec
); writeln(t); alebo aj write(t, reťazec
,#13#10);
- v nasledujúcom príklade využijeme
Memo1.Lines.LoadFromFile(meno_súboru) pomocou
ktorého zapíšeme obsah celého
súboru do textovej plochy
Príklad: Vytvoríme súbor
text.txt z písmen 'a' až 'z' a vypíšeme
jeho obsah do Textovej plochy
|
procedure TForm1.Button1Click(Sender: TObject);
var
t:TextFile;
z,z1:char;
begin
AssignFile(t,'text.txt'); Rewrite(t);
for z:='a' to 'z' do begin
for z1:=z to 'z' do write(t,z1);
writeln(t);
end;
CloseFile(t);
Memo1.Lines.LoadFromFile('text.txt');
end;
|
Príklad: Vytvoríme kópiu súboru
unit1.pas do súboru text.txt
|
var
t1,t2:TextFile;
z:char;
begin
AssignFile(t1,'unit1.pas'); Reset(t1);
AssignFile(t2,'text.txt'); Rewrite(t2);
while not Eof(t1) do
if Eoln(t1) then begin
readln(t1); writeln(t2);
end
else begin
read(t1,z);
// spracuj načítaný znak
write(t2,z);
end;
CloseFile(t1);
CloseFile(t2);
Memo1.Lines.LoadFromFile('text.txt');
end;
|
iný variant kopírovania súboru
- nevšímame si konce riadkov a prerábame
malé písmená na veľké
|
var
t1,t2:TextFile;
z:char;
begin
AssignFile(t1,'unit1.pas'); Reset(t1);
AssignFile(t2,'text.txt'); Rewrite(t2);
while not Eof(t1) do begin
read(t1,z);
if (z>='a') and (z<='z') then
z:=char(ord(z)-ord('a')+ord('A')); // alebo dec(z,32);
write(t2,z);
end;
CloseFile(t1);
CloseFile(t2);
Memo1.Lines.LoadFromFile('text.txt');
end;
|
Pozn:
- na prerábanie malých písmen
na veľké môžeme použiť štandardnú
znakovú funkciu z:=upcase(z);
NDÚ:
- postupnosť medzier nahraď 1 medzerou
- vyhoď len medzerové riadky
- vyhoď medzery na konci riadkov
- postupnosť znakov 'end' nahraď '***'
Reset a Rewrite na ten istý súbor
na disku
- nepredvídateľné - rôzne verzie
Pascalu reagujú rôzne (Delphi hlási
I/O chybu)
Čítanie čísel
- pomocou read môžeme čítať aj čísla
(celé aj reálne), ale v súbore
musia byť tieto čísla ukončené medzerou,
koncom riadka alebo tabulátorom (znak s kódom
#9)
- príkaz read(t, číselná_premenná
) najprv preskočí všetky medzerové znaky
(medzera, <Eoln> alebo #9), potom prekonvertuje
znaky zo vstupu na číslo a ak je číslo
ukončené nemedzerovým znakom (napr. ','
alebo ';'), tak vyhlási chybu Invalid numeric
format
- čítanie čísla na konci súboru
(t.j. platí eof, ale aj ak sú tam len
medzery) vráti hodnotu 0 - treba sa tohoto vyvarovať!
- profesionálny softvér
takýto read na čítanie čísel nepoužíva, lebo
chyba v súbore spôsobí chybovú
správu (výpočet je ďalej nekorektný)
- vy môžete používať takéto čítanie,
len ak je v zadaní výslovne povedané,
že je súbor korektný a číslo je
ukončené medzerovým znakom
- neskôr uvidíme aj iný (bezpečný)
spôsob čítania čísel
Príklad: Program nájde maximálne
číslo v súbore celých čísel:
|
var
t:TextFile;
cislo,max:integer;
begin
AssignFile(t,'text.txt'); Reset(t);
max:=-maxint; // preddefinovaná konštanta 2147483647
while not Eof(t) do begin // vyskúšajte SeekEof
read(t,cislo);
if cislo>max then max:=cislo;
end;
CloseFile(t);
if max=-maxint then
Memo1.Lines.Add('súbor je prázdny')
else
Memo1.Lines.Add('maximálne číslo v súbore je '+IntToSTr(max));
end;
|
- ak súbor obsahoval napr. len prázdny
riadok (alebo len medzery), tak program vypíše,
že maximum bolo 0
- toto isté sa stane, ak súbor obsahuje
len záporné čísla a za posledným
číslom sú ešte nejaké medzerové
znaky - funkcia Eof(t) za posledným číslom
vráti false - ešte nie je koniec súboru,
ale už tam nie je žiadne číslo, teda načíta
sa hodnota 0
- v takejto situácii môžeme namiesto
Eof(t) použiť štandardnú funkciu SeekEof(t),
ktorá skôr ako odpovie na stav súboru,
odfiltruje všetky medzerové znaky
- podobne existuje SeekEoln(t), ktorá testuje
koniec riadka, ale najprv odfiltruje medzery a tabulátory
(znaky s kódom #9)
- Pozn. Pri použití funkcie SeekEoln(t) a
SeekEof(t) sa odignorujú všetky medzery, tabulátory
a v prípade SeekEof(t) i konce riadkov. Preto
tieto
funkcie nie sú veľmi vhodné pri čítaní
znakov, používame ich hlavne pri čítaní
čísel z textového súboru.
Zápis čísel
- pomocou write(t, ...) môžeme do textového
súboru zapisovať aj čísla (hodnoty
číselných výrazov)
- celé čísla sa zapíšu bez
medzery pred číslom aj za číslom,
t.j. write(t,i,i+1) pre i=17 zapíše 1718
- reálne čísla sa do súboru
zapisujú v
semilogaritmickom tvare s medzerou pred číslom
Príklad: Zo súboru text1.txt budeme
kopírovať všetky čísla do súboru
text2.txt, pričom ich budeme zaraďovať po troch
do riadka:
|
var
t1,t2:TextFile;
cislo,pocet:integer;
begin
AssignFile(t1,'text1.txt'); Reset(t1);
AssignFile(t2,'text2.txt'); Rewrite(t2);
pocet:=0;
while not SeekEof(t1) do begin
read(t1,cislo);
if pocet=3 then begin
writeln(t2); pocet:=1;
end
else begin
if pocet>0 then write(t2,' ');
inc(pocet);
end;
write(t2,cislo);
end;
CloseFile(t1);
CloseFile(t2);
Memo1.Lines.LoadFromFile('text2.txt');
end;
|
Príklad: V súbore sú reálne
čísla, máme zistiť počet čísel,
ktoré majú hodnotu menšiu ako je priemer
všetkých čísel v súbore
|
var
t:TextFile;
cislo,suma,priemer:real;
pocet:integer;
begin
AssignFile(t,'text1.txt'); Reset(t);
// priradenie vstupného súboru a jeho otvorenie na čítanie
suma:=0; pocet:=0;
while not SeekEof(t) do begin
read(t,cislo);
suma:=suma+cislo; inc(pocet);
end;
Reset(t); // !!! nastavenie pozície na začiatok súboru !!!
priemer:=suma/pocet; pocet:=0;
// pocet už nepotrebujeme
while not SeekEof(t) do begin
read(t,cislo);
if cislo<priemer then inc(pocet);
end;
Memo1.Lines.Add('počet podpriemerných='+IntToSTr(pocet));
end;
|
Formátovací parameter vo write
- za znakom alebo znakovým reťazcom
write(t,'*':10); označuje,
že znak sa vypíše na šírku 10, t.j.
najprv 9 medzier a potom '*' write(t,'delphi':3); nakoľko
reťazec je dlhší ako formátovací
parameter, zapíše sa kompletný reťazec,
t.j. ignoruje sa formát
- formátovací parameter za celým
číslom označuje šírku, do ktorej
sa má zapísať číslo, ak by
nevošlo do danej šírky, formát sa
ignoruje
write(t,25*25:5); zapíše
dve medzery, za ktoré dá číslo
625
- formátovací parameter za reálnym
číslom tiež označuje šírku, číslo
sa vypíše v semilogaritmickom tvare;
druhý formátovací parameter
označuje počet desatinných miest
write(t,sin(2):15); zapíše
9.092974E-0001 write(t,cos(2):7:4); zapíše
-0.4161
Textové súbory a korytnačky
Pripomeňme príkaz case, ktorý pre ordinálnu
hodnotu zabezpečí vykonanie nejakej vetvy podľa
príslušnej konštanty – všeobecne:
- Príklad: daný je súbor,
v ktorom sú príkazy pre korytnačku:
d,l,p + číslo, napr.
d
100 p 120 d 100 p 120 d 100 pričom písmená
a čísla sú navzájom oddelené
aspoň jednou medzerou alebo novým riadkom
korytnačka interpretuje tento súbor:
|
var
t:TextFile;
k:TKor;
z:char;
p:integer;
begin
AssignFile(t,'kor1.txt'); Reset(t);
k:=TKor.Create;
while not eof(t) do begin
if eoln(t) then begin readln(t); z:=' '; end
else read(t,z);
if z<>' ' then read(t,p);
case z of
'd': k.dopredu(p);
'p': k.vpravo(p);
'l': k.vlavo(p);
end;
end;
CloseFile(t);
end;
|
vylepšená verzia s použitím SeekEof(t):
|
var
t:TextFile;
k:TKor;
z:char;
p:integer;
begin
AssignFile(t,'kor1.txt'); Reset(t);
k:=TKor.Create;
while not SeekEof(t) do begin
read(t,z,p);
case z of
'd': k.dopredu(p);
'p': k.vpravo(p);
'l': k.vlavo(p);
end;
end;
CloseFile(t);
end;
|
Príklad: v súbore sú slová
dopredu, vpravo, vlavo, ph, pd (z každého aspoň
2 písmená) – za niektorými nasleduje
číslo:
|
var
t:TextFile;
k:TKor;
z,z1,z2:char;
p:integer;
begin
AssignFile(t,'kor2.txt'); Reset(t);
k:=TKor.Create;
while not SeekEof(t) do begin
read(t,z,z1); z2:=z1;
while (z2<>' ') and not eoln(t) do read(t,z2);
if (z='d') and (z1='o') then begin
read(t,p); k.dopredu(p);
end
else if (z='v') and (z1='p') then begin
read(t,p); k.vpravo(p);
end
else if (z='v') and (z1='l') then begin
read(t,p); k.vlavo(p);
end
else if (z='p') and (z1='h') then
k.PH
else if (z='p') and (z1='d') then
k.PD;
end;
CloseFile(t);
end;
|
Pozn.
- riešenie tejto úlohy môžeme zjednodušiť,
ak si budeme všímať len 2. písmeno
slov - v tomto prípade by sa dal použiť príkaz
case
Príklad
- počas kreslenia nejakého obrázka
pomocou korytnačky (napr. kvetinka) si budeme do
textového súboru zapamätávať
momentálne súradnice korytnačky, t.j.
(k.X,k.Y) - zrejme stačí sledovať príkaz
dopredu
vytváranie súboru s postupnosťou
súradníc:
|
var
t:TextFile;
k:TKor;
procedure poly(n,d,u:integer);
begin
while n>0 do begin
k.dopredu(d);
writeln(t,k.X:0:2,' ',k.Y:0:2);
k.vpravo(u); dec(n);
end;
end;
var
i:integer;
begin
AssignFile(t,'kor3.txt'); Rewrite(t);
k:=TKor.Create;
poly(1,100,0);
for i:=1 to 7 do begin
poly(9,5,10); k.vpravo(90);
poly(9,5,10); k.vpravo(90);
k.vpravo(360/7);
end;
CloseFile(t);
end;
|
korytnačka interpretuje súbor z predchádzajúceho
príkladu:
|
var
t:TextFile;
k:TKor;
x,y:real;
begin
AssignFile(t,'kor3.txt'); Reset(t);
k:=TKor.Create;
while not eof(t) do begin
readln(t,x,y);
k.ZmenXY(x,y);
end;
CloseFile(t);
end;
|
to isté, ale do súboru sa ukladajú
relatívne posuny korytnačky:
|
var
t:TextFile;
k:TKor;
x,y:real;
procedure poly(n,d,u:integer);
begin
while n>0 do begin
k.dopredu(d);
writeln(t,k.X-x:0:2,' ',k.Y-y:0:2);
x:=k.X; y:=k.Y;
k.vpravo(u); dec(n);
end;
end;
var
i:integer;
begin
AssignFile(t,'kor4.txt'); Rewrite(t);
k:=TKor.Create; x:=k.X; y:=k.Y;
poly(1,100,0);
for i:=1 to 7 do begin
poly(9,5,10); k.vpravo(90);
poly(9,5,10); k.vpravo(90);
k.vpravo(360/7);
end;
CloseFile(t);
end;
|
korytnačku vygenerujeme
na náhodnú pozíciu a určíme náhodnú veľkosť obrázka
(random(23)/10+0.3):
|
var
t:TextFile;
k:TKor;
x,y,f:real;
i:integer;
begin
AssignFile(t,'kor4.txt');
k:=TKor.Create;
randomize;
for i:=1 to 10 do begin
Reset(t);
k.PresunXY(random(400),random(300));
f:=random(23)/10+0.3; // náhodná veľkosť
while not eof(t) do begin
readln(t,x,y);
k.zmenxy(k.X+x*f,k.Y+y*f);
end;
end;
CloseFile(t);
end;
|
|