Föreläsning 7: Filer
Genomgånget på FÖ:
Undantag:
Liten mall för undantag ("exceptions") och vad man har
det till. Dock inte med betoning på allt, men det
väsentliga är upptaget.
Filer:
P. Open(filvar, mode, filnamn);
kraschar om filen ej finns!
mode = In_File, Out_File eller Append_File
filvar : File_Type;
P. Create(filvar, mode, filnamn);
se Open (med undantag att det ej kraschar!)
P. Close(filvar);
P. Delete(filvar);
filen måste vara öppen!
P. Reset(filvar);
ej tagit upp denna, men det fixar ni.
P. Get(filvar, X);
P. Get_Line(filvar, Str, X);
P. Skip_Line(filvar);
som för tangentbord (endast textfiler).
P. Put(filvar, X);
P. Put_Line(filvar, Str);
P. New_Line(filvar);
som för skärm (endast textfiler).
F. End_Of_File(filvar) return Boolean;
gäller för alla typer av filer.
utan filvar => tangentbord.
F. End_Of_Line(filvar) return Boolean;
endast textfiler.
utan filvar => tangentbord.
kraschar om filslut utan radslut (om ej tom
fil förstås).
Följande exempel visat:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Copy_File is
Original : File_Type;
Copy : File_Type;
Ch : Character;
begin
Open(Original, In_File, "A.TXT");
Create(Copy, Out_File, "B.TXT");
while not End_Of_File(Original) loop
while not End_Of_Line(Original) loop
Get(Original, Ch);
Put(Copy, Ch);
end loop;
Skip_Line(Original);
New_Line(Copy);
end loop;
Close(Original);
Close(Copy);
end Copy_File;
Jag talade kort om vad man använde filer till, men jag tror att det är bra att ta det en gång till. Säg
något i stil med att man vill kunna köra programmet och sen avsluta det och sen nästa gång vill man
kunna fortsätta från den plats man slutade (d.v.s. man vill inte mata in alla data igen).
Övning på lektion
Detta lektionsmaterial är omfattande. Räkna inte med att hinna med allt som står utan låt det ta den
tid det tar. Man bör hinna med två uppgifter. Den tredje kan man ju alltid lämna som
hemuppgift. :-)
Uppgift 1: Antag att vi inte har tillgång till "Append_File"
i den kompilator som finns. Hur gör vi om vi vill
utföra en "Append" iallafall? Vi har tillgång till
allt utom just "Append_File".
Detta problem är bra till att diskutera hur man löser problem (d.v.s. det som kursen går ut på). Man
kan här ta upp saker som att man kan "använda brain storming", "tänka på svenska", "post-itlappar", "stegvis förfining", "skjuta problem framför sig" m.m.
Vi kan börja med att skriva ett huvudprogram som visar vad det är vi vill komma fram till.
with Ada.Text_IO;
use Ada.Text_IO;
procedure Xxx is
Data_File : File_Type;
begin
­­ Vi borde skriva:
­­ Open(Data_File, Append_File, "A.TXT");
­­ men vi har ju kompilatorbugg för att få fram
­­ något bra exempel att arbeta med på lektion. :­)
­­ OBS! Append_File fungerar utmärkt i Gnat.
Append(Data_File, "A.TXT");
Put_Line(Data_File, "Lite extra text ...");
Close(Data_File);
end Xxx;
Studenterna kan ju få hjälpa till med vilken datatyp som skall användas m.m., men det är nog bra att
få fram budskapet vad uppgiften går ut på (det brukar finnas en del funderingar på vad det är
annars).
Vi skall alltså skapa oss en 'Append' som ser till att filen kan skrivas på och att de data som redan
låg på filen inte försvinner. Bra att veta att "Out_File" innebär att filens innehåll "raderas". Det går
inte att öppna för läsning och läsa från början till slut och sen tro att det bara är att fortsätta skriva.
Det brukar bli en del diskussion om hur man skall göra och det får det väl bli. Försök att vara öppen
och skriv upp det de säger så kanske man till slut kan få en lista på bra saker som skall göras.
Då någon kommit på att det går att kopiera filen till en ny fil och sen kopiera tillbaka den för att sen
lämna den öppen för vidare skrivning har vi kommit fram till lösningen. Det är inte tillåtet att spara
alla data i ett fält eller annat (filer kan vara alldeles för stora).
Vi antecknar vad som måste göras (inte alls säkert att förslagen kommer i den rätta ordningen, men
vi styr lite så blir det bra):
1) Öppna originalfilen (vi kallar den A) för läsning.
2) Skapa en ny fil (vi kallar den B) för skrivning.
3) Kopiera A -> B.
4) Stäng A och B.
5) Öppna B för läsning.
6) Öppna A för skrivning.
7) Kopiera B -> A.
8) Ta bort B.
9) KLART!
Översätt detta till kod! Det borde inte vara så svårt. :-)
procedure Append(File : in out File_Type;
Filename : String) is
Temp : File_Type;
begin
Open(File, In_File, Filename);
Open(Temp, Out_File, "TMP.TMP");
Copy(File, Temp);
Close(Temp);
Close(File);
Open(Temp, In_File, "TMP.TMP");
Open(File, Out_File, Filename);
Copy(Temp, File);
Delete(Temp);
end Append;
Vi behöver en extra procedur som kopierar (om vi inte var klantiga när vi skrev "Append").
procedure Copy(Original, Copy : in File_Type) is
Item : Character;
begin
while not End_Of_File(Original) loop
while not End_Of_Line(Original) loop
Get(Original, Item);
Put(Copy, Item);
end loop;
Skip_Line(Original);
New_Line(Copy);
end loop;
end Copy;
Denna borde de klara som en dans då vi redan haft den på FÖ. Observera att parametern har endast
"in". Detta gäller generellt för filer om man inte ändrar status (d.v.s. öppnar eller stänger). Se i
"Append": parametern har "in out" för att vi skall öppna den för skrivning från huvudprogrammets
vy. Vad händer om vi istället skall lägga till data i en binär heltalsfil? Vi börjar med
huvudprogrammet.
with Ada.Sequential_IO;
procedure Xxx is
package Integer_IO is
new Ada.Sequential_IO(Integer);
use Integer_IO;
Data_File : File_Type;
begin
Append(Data_File, "A.INT");
Write(Data_File, 42); ­­ Talet 42 skrivs på filen.
Close(Data_File);
end Xxx;
I "Append" händer inget alls (algoritmen helt ok alltså). Dock händer det lite i "Copy".
procedure Copy(Original, Copy : in File_Type) is
Item : Integer; ­­ Måste stämma med filtypen!
begin
while not End_Of_File(Original) loop
Read(Original, Item);
Write(Copy, Item);
end loop;
end Copy;
Observera att det inte finns några operationer "End_Of_Line", "New_Line" och "Skip_Line" på
binära filer och att "Get"/"Put" heter "Read"/"Write" istället.
Uppgift 2: Läs in de två sista talen från en fil, summera
dessa och skriv sedan ut summan i slutet av samma
fil. Om det inte existerar några tal eller bara
finns ett tal på filen ska programmet se till att
filen innehåller de två talen 1 och 1 istället.
Vi kan anta att det alltid finns en fil i första
problemet.
Det problem som finns är alltså att räkna antalet tal som finns och bara lagra de två sista hela tiden.
Vi antar att det är en binär heltalsfil för att öva mer på detta.
with Ada.Sequential_IO;
procedure Xxx is
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
package Integer_IO is
new Ada.Sequential_IO(Integer);
use Integer_IO;
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
type Integer_Array is
array (1..2) of Integer;
Data_File : File_Type;
Last_Numbers : Integer_Array;
No_Of_Numbers : Integer := 0;
begin
Open(Data_File, In_File, "TAL.INT");
while not End_Of_File(Data_File) loop
if No_Of_Numbers < 2 then
No_Of_Numbers := No_Of_Numbers + 1;
end if;
Last_Numbers(1) := Last_Numbers(2);
Read(Original, Last_Numbers(2));
end loop;
Close(Data_File);
if (No_Of_Numbers = 2) then
Open(Original, Append_File, "TAL.INT");
Write(Original, Sum(Last_Numbers));
else
Open(Original, Out_File, "TAL.INT");
Write(Original, 1);
Write(Original, 1);
end if;
Close(Original);
end Xxx;
Man kan hänvisa funktionen "Sum" till tidigare anteckningar (vi tog väl upp något sådant på
lektionen om fält).
Uppgift 3: Samma problem som i uppgift 2, men om filen inte
existerar ska programmet skapa filen och skriva ut
de två talen (1 och 1) på den.
Ja, nu behövs det felhantering med undantag ("exceptions") om man skall lösa detta problem. Enda
ändringen blir i detta fall att vi vill fånga felet som kan uppstå för att filen inte existerar och detta
kan vi göra i slutet av programmet.
procedure Xxx is
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
package Integer_IO is
new Ada.Sequential_IO(Integer);
use Integer_IO;
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
type Integer_Array is
array (1..2) of Integer;
Data_File : File_Type;
Last_Numbers : Integer_Array;
No_Of_Numbers : Integer := 0;
begin
Open(Data_File, In_File, "TAL.INT");
while not End_Of_File(Data_File) loop
if No_Of_Numbers < 2 then
No_Of_Numbers := No_Of_Numbers + 1;
end if;
Last_Numbers(1) := Last_Numbers(2);
Read(Original, Last_Numbers(2));
end loop;
Close(Data_File);
if (No_Of_Numbers = 2) then
Open(Original, Append_File, "TAL.INT");
Write(Original, Sum(Last_Numbers));
else
Open(Original, Out_File, "TAL.INT");
Write(Original, 1);
Write(Original, 1);
end if;
Close(Original);
exception
when Name_Error =>
Create(Data_File, Out_File, "TAL.INT");
Write(Original, 1);
Write(Original, 1);
Close(Original);
end Xxx;