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;