Date post: | 01-May-2015 |
Category: |
Documents |
Upload: | fiorenza-giovannini |
View: | 213 times |
Download: | 0 times |
In Pascal è possibile avere oltre ai file testo, cioè file di char, anche file di interi, stringhe, records. Unica condizione è quella che se un file è ad esempio dichiarato di interi esso non può contenere che interi.Tutti i file di tipo non-testo sono detti file binari.
La estensione di un file testo è NomeFile.txtLa estensione di un file binario è NomeFile.dat
La sintassi di un file binario è diversa da quella di un file testo.
identificatore = FILE OF type
PROGRAM FileTesto(output, Teresa);VAR
Teresa:text;Ch:char;
BEGINEND.
PROGRAM FileBinari(output, Ints,Reals,Records);TYPE String30=STRING[30];
NameType=RECORDFirst,Middle,Last:String30;
END; IntsFile=FILE OF integer; RealsFile=FILE OF real; RecFile=FILE OF NameType;VAR
Ints:IntsFile;Reals:RealsFile;Names:RecFile;
BEGINEND.
Preparazione alla lettura del filereset(NomeFile) es. reset(Reals)
Preparazione alla scrittura del filerewrite(NomeFile) es. rewrite(Reals)
Il controllo di fine file viene eseguito come per i text file con la funzione <eof> eof(NomeFile)
Essendo i dati scritti uno di seguito all’altro non esiste più l’<eoln>.
Nel caso del RecFile i record NameType=RECORD
descritti da First,Middle,Last:String30;
avremo
La lettura avviene record per record, non stringa per stringa
Giulio Luca Rossi Carlo Maria Bianchi Gian Giacomo Verdi ……………..……………..
Carlo Maria Bianchi
Area Dati
AName.First CarloAName.Middle MariaAName.Last Bianchi
E’ possibile leggere anche più di un valore alla volta da un file.Esempio
read(Reals, R1, R2, R3,….)
Per leggere un intero file si può far uso dell’ <eof>Esempioreset(Ints);WHILE NOT eof(Ints) DO
BEGINread(Ints, Intero);elabora(Intero)
END;
L’uso dell’ <eoln> produce un errore di sintassi.
E’ possibile scrivere in un file binario se ovviamente è stato preparato per la scrittura.Esempiowrite(Ints, AnInt, 3*AnInt);write(Reals, Re1, 3.1416);write(Names, AName);write(Names, Name1, Name2);
Come si vede anche più di un dato può contemporaneamente essere scritto in un file binario.
Attenzione !!!Ogni elemento che si scrive deve essere dello stesso Type del file.Quindi sono sbagliate le scritture del tipowrite(Ints, Re1, 3.2*Re1);write(Reals, AnInt, AnInt DIV 2);write(Names, Aname.First, Aname.Middle, Aname.Last);
Errore !!!!!!
Non essendoci <eoln> non è permesso il writeln.
TYPE String30=STRING[30];
NameType=RECORDFirst,Middle,Last:String30;
END; IntsFile=FILE OF integer; RealsFile=FILE OF real; RecFile=FILE OF NameType;VAR
Ints:IntsFile;Reals:RealsFile;Names:RecFile;
COPIA DI FILE BINARI
Supponiamo di avere due file binary AFile e Bfile aventi lo stesso SomeType le cui componenti sono del tipo ComponentType.
WHILE NOT eof(Afile) DOread(Afile, Acomponent)write(Bfile, Acomponent)
PROCEDURE CopyFile(VAR InNames, OutNames:RecFile);VAR
AName:NameType; BEGIN
WHILE NOT eof(InNames) DOread(InNames, AName);write(OutNames, AName)
ENDEND;
Si noti che con una sola operazione di read o write si possono leggere molti valori contemporaneamente se questo è previsto dalla struttura dei file in gioco.Ad esempio un record con 20 campi può essere scritto con una sola operazione e non campo per campo.
Nel caso in cui si vogliono dare i valori di un record campo per campo, ad esempio da tastiera, allora si può adoperare la seguente procedura:
WITH RecordVar DOintroduci il valore del campo
write(OutFile, RecordVar)
PROCEDURE WriteAName(VAR OutFile:NameFile);VAR
Aname: NameType;BEGIN
WITH Aname DOBEGIN
write(‘ First Name: ‘);readln(First);write(‘ Middle Name: ‘);readln(Middle );write(‘ Last Name: ‘);readln(Last );
END;write(OutFile, AName)
END;
TYPE String30=STRING[30];
NameType=RECORDFirst,Middle,Last:String30;
END; IntsFile=FILE OF integer; RealsFile=FILE OF real; RecFile=FILE OF NameType;VAR
Ints:IntsFile;Reals:RealsFile;Names:RecFile;
TYPEStringa20 = STRING[20]
RisultatiArray =ARRAY[1..TotaleProve] OF integer;
AnagraficaRecord = RECORDCognome,Nome : Stringa20END;
DataRecord = RECORDGiorno,Mese,Anno : integerEND;
StuRecord = RECORD Anagrafe :AnagraficaRecord ;
Nascita:DataRecord ; Matricola :StringaNome; AnnoCorso:StringaNome; Risultati :RisultatiArray ; Media:real;
END;
Studente
Anagrafe Nascita Matricola
Cognome Nome AnnoMeseGiorno
AnnoCorso Risultati Media
CONST MaxStud=150;
TYPEStuRecord = RECORD……………….END;
ClassArray=ARRAY[1..Maxstud] OF StuRecord;StuRecFile=FILE OF StuRecord;VAR
InFile:StuRecFile;AClass: ClassArray;TotalStudents:integer;
Procedura per la costruzione di un Array di record a partire da un file binario.
PROCEDURE FillClass(VAR AClass: ClassArray; VAR TotalStudents: integer; VAR InFile: StuRecFile);
VARAStudent: StuRecord;
BEGINTotalStudents:=0;reset(InFile);WHILE NOT eof(InFile) DOBEGIN
TotalStudents:= TotalStudents+1;read(InFile, AStudent);Aclass[TotalStudents]:=AStudent
ENDEND;
Supponiamo di introdurre meno di MaxStud record
StuRecord = RECORD Anagrafe :AnagraficaRecord ;
Nascita:DataRecord ; Matricola :StringaNome; AnnoCorso:StringaNome; Risultati :RisultatiArray ; Media:real;
END;
AGGIORNAMENTO DI FILE DI RECORD BINARI
Dati due File di record binari ordinati, fare il merge del primo nel secondo producendo un terzo file ordinato.
Corso ProgrammazioneSessione Estiva
CSPE99
Corso ProgrammazioneSessione Invernale
CSPI99
Corso ProgrammazioneCSP99
Semester OldMaster
NewMaster
merge Sessione StuRecord
Sessione StuRecord
StuRecord
MStuRec
MStuRec
Pseudo Codice di UpdateMerge
AssignFiles(LaSessione, Semester, OldMaster, NewMaster);
Merge(LaSessione, Semester, OldMaster, NewMaster);
Risultati(Semester,OldMaster,NewMaster);
writeln(' Fine aggiornamento ')END.
MERGEPseudo codice
{Merge(LaSessione, Semester, OldMaster ,NewMaster}
prendi un record MStu1 da OldMaster {GetMasterRec(MStu1)}prendi un record MStu2 da Semester {GetSemesterRec(LaSessione,MStu2,Semester}
WHILE NOT sono finiti i due file DOwrite il record con “nome” più piccolo in NewMasterprendi un altro record dal file giusto {OldMaster o Semester}
copia gli ultimi record MStu1 e MStu2 in NewMaster {CopyLastTwo(MStu1,MStu2,NewMaster}
{CopyOldMasterRecs(OldMaster,NewMaster}copia ordinatamente i record dai file non esauriti in NewMaster
{CopySemesterRecs(LaSessione,Semester,NewMaster}Semester
OldMaster
NewMaster
StuRecord MStuRec
MStuRec
CONSTTotaleProve=100;TYPEStringa4 = STRING[4];Stringa10 = STRING[10];Stringa25 = STRING[25];RisultatiArray=ARRAY[1..TotaleProve] OF integer;
StuRecord = RECORD Cognome, Nome : Stringa25;
Nascita:Stringa10; Matricola:Stringa10; AnnoCorso:Stringa4; Risultati:RisultatiArray; Media:real;
END;MStuRecord= RECORD
Sessione:Stringa25;Info:StuRecordEND;
StuRecFile= FILE OF StuRecord;MStuRecFile= FILE OF MStuRecord;VAR
LaSessione: Stringa25; {semestre appena finito}Semester: StuRecFile; {file semestre appena finito}OldMaster:MStuRecFile; {file preesistente}NewMaster : MStuRecFile; {file aggiornato}
StuRecord
Nascita MatricolaCognome Nome AnnoCorso Risultati MediaSessione
MStuRecord
Semester OldMaster
NewMaster
SessioneStuRecord
SessioneStuRecord
StuRecord
MStuRec
MStuRec
AssignFiles
LaSessioneSemesterOldMasterNewMaster
Merge
LaSessioneSemesterOldMasterNewMaster
OldMaster
GetMasterRec
MStu1
GetSemesterRec
Semester MStu2
Stu1GoesFirst
writewrite
GetMasterRec GetSemesterRec
Vero Falso
MStu1.InfoMStu2.Info
OldMaster MStu1 Semester MStu2
NewMaterMStu1
NewMaterMStu2
CopyLastTwo
NewMaterMStu1MStu2
CopySemesterRecs
OldMasterNewMaster
CopyMasterRecs
SemesterNewMaster
Risultati
SemesterOldMasterNewMaster
PROCEDURE AssignFiles(VAR LaSessione:Stringa25;VAR Semester:StuRecFile;VAR OldMaster, NewMaster : MStuRecFile);
{si legge la sessione da tastiera, si aprono i file Semester, OldMaster, in lettura e NewMaster in scrittura }VAR
CourseName, InvernaleOEstiva,Anno: Stringa25;BEGIN
write(' Nome corso= ');readln(CourseName); {es. PRO}write(' Sessione ("Invernale: i" o "Estiva: e")): ');readln(InvernaleOEstiva); {es. e}write(' Anno (due digiti): ');readln(Anno); {es. 00}LaSessione:= InvernaleOEstiva+ Anno; {es. e00}assign(Semester, CourseName+'e'+'.dat'); {es. PROe00.dat}IF InvernaleOEstiva='e' THEN
assign(OldMaster, CourseName+’i'+'00.dat')ELSE
assign(OldMaster, CourseName+'e'+'00.dat'); {es. PROe.dat}assign(NewMaster, CourseName+ InvernaleOEstiva +'new.dat'); {es. PROenew.dat}
reset(OldMaster);reset(Semester);rewrite(NewMaster)
END;
Semester OldMaster
NewMaster
SessioneStuRecord
SessioneStuRecord
StuRecord
MStuRec
MStuRec
PROCEDURE Merge (LaSessione:Stringa25; VAR Semester:StuRecFile;VAR OldMaster, NewMaster : MStuRecFile);
VARAstu:StuRecord; {variabile record letta da Semester}MStu1, MStu2: MStuRecord; {variabile record letta da OldMaster e quella formata da AStu e LaSessione}BEGINGetMasterRec(MStu1,OldMaster);GetSemesterRec(LaSessione,MStu2,Semester);WHILE NOT eof(OldMaster) AND NOT eof(Semester) DOBEGIN IF Stu1GoesFirst(MStu1.Info,Mstu2.Info) THEN BEGIN write(NewMaster,MStu1); GetMasterRec(MStu1,OldMaster) END ELSE BEGIN write(NewMaster,MStu2); GetSemesterRec(LaSessione,MStu2,Semester) END;END; CopyLastTwo(MStu1,MStu2,NewMaster); CopyOldMasterRecs(OldMaster,NewMaster); CopySemesterRecs(LaSessione,Semester,NewMaster); close(Semester); close(OldMaster); close(NewMaster)END;
Semester OldMaster
NewMaster
SessioneStuRecord
SessioneStuRecord
StuRecord
MStuRec
MStuRec
MStuRecord= RECORDSessione:Stringa25;Info:StuRecordEND;
Necessario perché il Get... avviene dopo la write
PROCEDURE GetMasterRec(VAR MStu:MStuRecord;VAR OldMaster:MStuRecFile);BEGINIF NOT eof(OldMaster) THEN
read(OldMaster,MStu)END;
PROCEDURE GetSemesterRec(LaSessione:Stringa25;VAR MStu:MStuRecord; VAR Semester:StuRecFile);BEGIN
IF NOT eof(Semester) THEN BEGIN read(Semester,MStu.Info); MStu.Sessione:=LaSessione ENDEND;
StuRecord
Nascita MatricolaCognome Nome AnnoCorso Risultati MediaSessione
MStuRecord
Semester OldMaster
NewMaster
SessioneStuRecord
SessioneStuRecord
StuRecord
MStuRec
MStuRec
FUNCTION Stu1GoesFirst(Stu1,Stu2:StuRecord):boolean;BEGIN IF Stu1.Cognome<>Stu2.Cognome THEN Stu1GoesFirst:=Stu1.Cognome<Stu2.Cognome ELSE IF Stu1.Nome<>Stu2.Nome THEN Stu1GoesFirst:=Stu1.Nome<Stu2.Nome ELSE Stu1GoesFirst:=Stu1.Matricola<Stu2.MatricolaEND;
Stu1.Cognome<>
Stu2.Cognome
Stu1, Stu2
Stu1.Nome<>
Stu2.Nome
Stu1.Cognome<
Stu2.Cognome
Stu1.Nome<
Stu2.Nome
<>
=
<>
=
Vero
FalsoFalso
Vero
Vero
Stu1.Matricola<>
Stu2. Matricola
Falso
PROCEDURE CopyLastTwo(MStu1,MStu2:MStuRecord;VAR NewMaster:MStuRecFile);BEGIN IF Stu1GoesFirst(MStu1.Info,Mstu2.Info) THEN BEGIN write(NewMaster,MStu1); write(NewMaster,MStu2) END ELSE BEGIN write(NewMaster,MStu2); write(NewMaster,MStu1) END;
END;
PROCEDURE CopyOldMasterRecs(VAR OldMaster, NewMaster:MStuRecFile);VAR AName:MStuRecord;BEGIN WHILE NOT eof(OldMaster) DO BEGIN read(OldMaster,AName); write(NewMaster,AName) ENDEND;
PROCEDURE CopySemesterRecs(LaSessione:Stringa25; VAR Semester:StuRecFile;VAR NewMaster : MStuRecFile);
VAR MStu:MSTuRecord;BEGIN WHILE NOT eof(Semester) DO BEGIN read(Semester,MStu.Info); MStu.Sessione:=LaSessione; write(NewMaster,MStu) ENDEND;
PROCEDURE Risultati (VAR SemFile:StuRecFile; VAR OldFile,NewFile :MStuRecFile);VAR NameSem:StuRecord; NameOld:MStuRecord; NameNew:MStuRecord; BEGIN writeln(' Semestre Old New '); WHILE NOT eof(NewFile) DO BEGIN
IF eof(SemFile) THEN BEGIN NameSem.Cognome:=' -- '; NameSem.Nome:=' -- ' END ELSE read(SemFile,NameSem); IF eof(OldFile) THEN BEGIN NameOld.Info.Cognome:=' -- '; NameOld.Info.Nome:=' -- ' END ELSE read(OldFile,NameOld) END; read(NewFile,NameNew); writeln(NameSem.Cognome:6,' ',NameSem.Nome:4,' ', NameOld.Info.Cognome:6,' ', NameOld.Info.Nome:4,' ', NameNew.Info.Cognome:6,' ', NameNew.Info.Nome:4); END; END;
Semestre Old Newagazzi carlo brescia ugo agazzi carlobottazzi anna distante claudio bottazzi annanapoli giulio livorno giulia brescia ugoperna claudio venezia anna distante claudiovenezia elena viterbo giorgio livorno giulia -- -- -- -- napoli giulio -- -- -- -- perna claudio -- -- -- -- venezia anna -- -- -- -- venezia elena -- -- -- -- viterbo giorgioFine aggiornamento
Pseudo codice per il Merge e Update di due file InFile1 InFile2
GetRec(Rec1,InFile1)GetRec(Rec2,InFile2)
WHILE NOT eof(InFile1) AND NOT eof(InFile2) DO
IF Rec1.Key < Rec2.Key THEN write(OutFile,Rec1) GetRec(Rec1,InFile1)ELSE write(OutFile,Rec2) GetRec(Rec2,InFile2)
CopyLastTwo(Rec1,Rec2,OutFile) CopyRemainder(InFile1,OutFile} CopyRemainder(InFile2,OutFile}
close all files
Esercizio
Scrivere due procedure:
•una per creare un file di tipo Semester e un file di tipo OldMaster
•una per leggere gli stessi file.
I PUNTATORI
Allocazione StaticaDato un blocco ogni variabile è allocata in memoria quando inizia l’elaborazione del blocco e deallocata quando l’elaborazione di tutto il blocco termina.
Allocazione DinamicaQuando ogni variabile è allocata o deallocata in memoria durante l’elaborazione del blocco.
Il puntatoreUn puntatore è una variabile il cui valore rappresenta un indirizzo di memoria. Esso serve per creare o eliminare una variabile dinamica.
Variabile dinamicaE’ una variabile alla quale si assegna spazio in memoria durante l’elaborazione di un blocco.
identificatore type= ^ESEMPIO
TYPE DataType = RECORD
Giorno:1..31;Mese:1..12;
Anno:integer END;DataPunt=^DateType;IntPunt=^integer;
VAROggi:DataPunt;A,B:IntPunt;Domani:DataType;
Si noti che la variabile Oggi, così come la variabile IntPunt, non assume i tre valori del record, o il valore di intero, a cui fa riferimento ma solo quello dell’indirizzo di memoria a partire del quale vi sono eventualmente i valori
Variabile Anonima: una variabile alla quale si accede solo tramite un puntatore
Variabile Nominata: una variabile alla quale si accede tramite un nome
Per assegnare un indirizzo a una variabile puntatore si usa la procedura new:es. new(Oggi)
TYPE DataType = RECORD
Giorno:1..31;Mese:1..12;
Anno:integer END;DataPunt=^DateType;IntPunt=^integer;
VAROggi:DataPunt;A,B:IntPunt;
Spazio di memoria assegnatoPrima di fare la chiamata new(Oggi)Oggi^ ?
Dopo la chiamata new(Oggi)Oggi^ ? ? ?
Per assegnare dei valori alla variabile dinamica Oggi
new(Oggi)Oggi^
read(Oggi^.Giorno, Oggi^.Mese, Oggi^.Anno)Enter 21 11 2000
Oggi^ .Giorno .Mese .Anno
? ? ?
21 11 2000
TYPE DateType = RECORD
Giorno:1..31;Mese:1..12;
Anno:integer END;
DataPunt=^DateType;IntPunt=^integer;VAROggi:DataPunt;A,B:IntPunt;
new(A);new(B);new(Oggi);A^:=5;B^:=7;write(‘Dammi la data (giorno mese anno): ‘);WITH Oggi^ DO
readln(Giorno, Mese, Anno)
Dammi la data (giorno mese anno) : 21 11 2000
21 11 2000Oggi^
5A^
7B^
Gli unici operatori che si applicano alle variabili puntatori sono:
operatore di assegnazione :=
operatori booleani = <>
TYPE DataType = RECORD
Giorno:1..31;Mese:1..12;
Anno:integer END;DataPunt=^DateType;IntPunt=^integer;
VAROggi:DataPunt;A,B,C:IntPunt;Domani:DataType;
L’operazione C :=B
12C^
7B^
X garbage
7L’operazione A^ :=B^
5A^
7B^
12C^
TYPEIntPunt=^integer;
VARA,B,C:IntPunt;
7A^
7B^
A^ : =5B^ := 7A^ :=B^IF (A^ = B^) AND (A <> B) THEN writeln(‘ I puntatori sono diversi ma i valori delle variabili puntate sono eguali’)
TYPEIntPunt=^integer;
VARA,B,C:IntPunt;
C :=BIF (C = B) THEN writeln(‘ I puntatori puntano alla stessa variabile’)
12C^
7B^
X garbage
dispose(C)C :=BIF (C = B) THEN writeln(‘ I puntatori puntano alla stessa variabile’)
12C^
B^ ?
12C^
B^
Come eliminare la spazzatura
In memoria esiste una speciale area detta run-time-heap dove sono allocate le variabili puntatore.
Nello heap ci sono le variabili puntatori create da new ad es. A B C D
Vi è un solo valore che una variabile puntatore può assumere e che non punta a nulla: NIL.
Es: D:= NIL;
Questa assegnazione serve per informare che per ora la variabile puntatore non è stata ancora associata a una variabile dinamica.
Si possono fare test per vedere se il puntatore è libero di essere associato ad una variabile dinamica.
Attenzione NIL non è assegnato per default.
L’istruzione C:=B mostra che possiamo assegnare memoria ad un puntatore senza fare uso di new questo ci permette di avere due puntatori che puntano alla stessa variabile dinamica, riducendo così lo spazio di memoria usato.
Possiamo quindi scrivere
new(B);B^:=18;B:=C Questa istruzione è valida solo se il puntatore C esiste.
Attenzione se a un puntatore è assegnato NIL, es. D:= NIL, non è possibile fare dispose(D).
Se abbiamo allocato memoria es.
new(D);D^:=5;D:= NIL
resta spazzatura
bisogna invece fare coem di seguito
new(D);D^:=5;dispose(D);D:= NIL;
NIL non elimina la spazzatura
Poichè possiamo assegnare memoria ad un puntatore senza fare uso di new questo ci permette di avere due puntatori che puntano alla stessa variabile dinamica, riducendo così lo spazio di memoria usato.
ARRAY DI PUNTATORI
Anagrafe ClienteN Info
ClientRecord
Cognome Nome
CONSTMaxStu=100;TotaleProve=5;TYPE
String25=STRING[25];String10=STRING[10];String4=STRING[4];RisultatiArray=ARRAY[1.. TotaleProve] OF integer
StuRecord = RECORD Cognome, Nome: String25; Nascita: String10; Matricola:String10; Risultati:RisultatiArray; Media:real; END;
ClientesFile=FILE OF ClientRecord;ClientPointer=^ClientRecord;PointerArray=ARRAY[0..MaxClients] OF ClientPointer;
VARClientsOnFile:ClientsFile;ByName, ByNo: PointerArray;TotalStu:integer;
Supponiamo che i ClienteN siano stringhe di lunghezza 5 tutte piene. Quindi avremo numeri tipo 00000 00123 23041
Useremo la stringa 00000 come sentinella.
PROBLEMALeggere il file Semester e realizzare due array di puntatori uno ordinato per nome (ByName) ed uno per matricola (ByMat).
L’array ByName contiene i puntatori agli studenti ordinati per nome.Vogliamo scrivere una funzione che faccia una ricerca di uno studente per numero di matricola sull’array ByMat.
1
mid
TotalStu
1
mid
TotalStu
ByName ByMat
050/714 21 22 23 30 27Abate Carlo 30/11/76 2000 25
050/734 28 22 28 30 27Carlini Anna 30/11/72 1999 27
050/514 30 21 23 30 27Zucchi Ugo 03/01/75 2000 24
Pseudo Codice
AssegnaFile(Semester)
OrganizzaDati(ByName,ByMat,TotalStu,Semester)
write('Dammi la matricola cercata: ');
readln(Matr);
Risultati(CercaStudente(ByMat,Matr,1,TotalStu));
PROGRAM ArrayPuntatori(input, output, Semester);
CONSTMaxStu=100;TotaleProve=100;TYPEStringa4 = STRING[4];Stringa10 = STRING[10];Stringa25 = STRING[25];RisultatiArray=ARRAY[1..TotaleProve] OF integer;
StuRecord = RECORD Cognome, Nome : Stringa25;
Nascita:Stringa10; Matricola:Stringa10; AnnoCorso:Stringa4; Risultati:RisultatiArray; Media:real;
END;
StuFile=FILE OF StuRecord;StuPointer=^StuRecord;PointerArray=ARRAY[0.. MaxStu] OF StuPointer;
VARSemester: StuFile; ByName, ByMat: PointerArray;TotalStu:integer;
Matr:Stringa10;
PROCEDURE Insert(NewElement:StuPointer; Candidate: integer; VAR ByMatP:PointerArray);BEGINWHILE (ByMatP[Candidate-1]^.Matricola > NewElement^.Matricola ) DOBEGIN
ByMatP[Candidate]:= ByMatP[Candidate-1]; Candidate:=Candidate-1
END;ByMatP[Candidate]:=NewElementEND;
PROCEDURE OrganizzaDati(VAR ByName,ByMat:PointerArray;VAR TotalStu:integer; VAR StuOnFile:StuFile);
VAR AStu:StuPointer;BEGINreset(StuOnFile);TotalStu := 0;new(ByMat[0]);ByMat[0]^.Matricola:='00000';WHILE NOT eof(StuOnFile) DO BEGIN
new(AStu);read(StuOnFile, AStu^);TotalStu := TotalStu + 1;ByName[TotalStu] := AStu;Insert(AStu, TotalStu, ByMat)
END;close(StuOnFile)END;
FUNCTION CercaStudente(VAR ByMatP: PointerArray;Numero:Stringa10; Lo,Hi:integer): StuPointer;
VARProbe:integer;BEGIN
IF Lo > Hi THEN CercaStudente :=NIL
ELSE BEGIN
Probe:=(Lo+Hi) DIV 2;IF ByMatP[Probe]^.Matricola = Numero THEN
CercaStudente:= ByMatP[Probe];ELSE IF ByMatP[Probe]^.Matricola < Numero THEN
CercaStudente:= CercaStudente(ByMat, Numero, Probe+1,Hi) ELSE
CercaStudente:= CercaStudente(ByMat, Numero, Lo,Probe-1) END
END;
PROCEDURE Risultati(MatrCand:Stupointer); BEGIN writeln(Matrcand^.Cognome,' ', MatrCand^.Nome,' ', MatrCand^.Matricola); END;
{******************** MAIN ******************}
BEGINassign(semester,'a:\probe.dat');OrganizzaDati(ByName,ByMat, TotalStu, Semester);readln;write('Dammi la matricola cercata: ');readln(Matr);Risultati(CercaStudente(ByMat,Matr,1,TotalStu));readlnEND.
Esercizio
Scrivere il programma completo per la gestione dei records attraverso array di puntatori ordinati per Nome, Matricola, Data di Nascita, Media.
Alcuni suggerimenti sui file
Evitare di usare il REPEAT … UNTIL quando si leggono file binari o testo.
reset(SomeFile)REPEAT
read(SomeFile,SomeComponent)elabora(SomeComponent)
UNTIL eof(SomeFile)reset(SomeFile)IF NOT eof(SomeFile) THENREPEAT
read(SomeFile,SomeComponent)elabora(SomeComponent)
UNTIL eof(SomeFile)
SBAGLIATO !!!!!!!!!!
CORRETTO !!!!!!!!!!
Non mettere mai un reset o un rewrite all’interno di un loop.
WHILE NOT eof(SomeFile) DO BEGIN
reset(SomeFile);read(SomeFile,SomeComponent)elabora(SomeComponent)
END;
SBAGLIATO !!!!!!!!!!
Ricordare che il valore di una variabile file cambia sempre quando si usano il read o il write, quindi le chiamate alle variabili file vanno sempre fatte per VAR e mai per valore.
Ricordare che readln e writeln si possono usare solo con i file testo e non con i file binari.
Quando si implementano procedure per la gestione di file realizzare sempre procedure per provare se i record o comunque i dati sono correttamente inseriti facendo le prove con pochi esempi.
Alcuni suggerimenti sui puntatori
Per il passaggio di parametri relativi ai puntatori valgono le stesse regole che si applicano per gli altri tipi di variabili.
Quando un puntatore è chiamato per valore viene fatta una copia locale del suo valore, cioè dell’indirizzo.
Quando un puntatore è chiamato per variabile viene prodotto un alias locale per il suo valore attuale, ricordando sempre che si tratta di indirizzi.
PROGRAM TestChiamatePuntatori;TYPEIntP=^integer;VARAnIntP:IntP;PROCEDURE ValCall(XP:IntP);BEGINXP^:=7;END;PROCEDURE VarCall(VAR XP:IntP);BEGINXP^:=7;END;
BEGINnew(AnIntP);AnIntP^:=5;ValCall(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= 7 Questo accade perché l’indirizzo passato dalla chiamata rimane lo stesso mentre il valore della variabile dinamica è cambiato. Questa chiamata equivale ad una chiamata per VAR su XP^.
BEGINnew(AnIntP);AnIntP^:=5;ValCallVar(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= 7 Il valore resta lo stesso essendo la chiamata precedente equivalente ad una chiamata per VAR su XP^.
PROGRAM TestChiamatePuntatori;TYPEIntP=^integer;VARAnIntP:IntP;PROCEDURE ValCall(XP:IntP);BEGINdispose(XP);END;PROCEDURE VarCall (VAR XP:IntP);BEGINdispose(XP);END;
BEGINnew(AnIntP);AnIntP^:=5;ValCall(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= non predicibile Questo accade perché l’indirizzo passato dalla chiamata viene deallocato. Poiché questo indirizzo nel main corrisponde anche a quello di AnInt^ il valore di AnInt^ ora non è più predicibile.
BEGINnew(AnIntP);AnIntP^:=5;VarCall (AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= non predicibile Come prima.
PROGRAM TestChiamatePuntatori;TYPEIntP=^integer;VARAnIntP:IntP;PROCEDURE ValCall(XP:IntP);BEGINdispose(XP);new(XP);END;PROCEDURE VarCall (XP:IntP);BEGINdispose(XP);new(XP);END;
BEGINnew(AnIntP);AnIntP^:=5;ValCall(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= non predicibile Questo accade perché l’indirizzo passato dalla chiamata viene deallocato. Poiché questo indirizzo nel main corrisponde anche a quello di AnInt^ il valore di AnInt^ ora non è più predicibile. Inoltre la memoria allocata da new diventa spazzatura non appena si esce dal blocco.
BEGINnew(AnIntP);AnIntP^:=5;VarCall (AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= non predicibile Come prima. Solo che ora non si crea spazzatura.
PROGRAM TestChiamatePuntatori;TYPEIntP=^integer;VARAnIntP:IntP;PROCEDURE ValCall(XP:IntP);BEGINXP:=NIL;END;PROCEDURE VarCall (XP:IntP);BEGINXP:=NIL;END;
BEGINnew(AnIntP);AnIntP^:=5;ValCall(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= 5 Questa istruzione riassegna un valore a XP. Ora XP e AnInt hanno valori differenti. Quando si esce da ValCall XP è perso e quindi AnInt resta =5. Non c’è spazzatura perché abbiamo usato il NIL.
BEGINnew(AnIntP);AnIntP^:=5;VarCall (AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
Output= 5 Questa istruzione riassegna a AnIntP il valore NIL. Quindi quando si esce da ValCall AnIntP^ non è predicibile inoltre a AnIntP è stato assegnato un nuovo valore per cui il precedente contenente 5 è diventato spazzatura.
PROGRAM TestChiamatePuntatori;TYPEIntP=^integer;VARAnIntP:IntP;PROCEDURE ValCall(XP:IntP);BEGINnew(XP);XP^:=7writeln(XP:1)END;PROCEDURE VarCall (XP:IntP);BEGINnew(XP);XP^:=7writeln(XP:1)END;
BEGINnew(AnIntP);AnIntP^:=5;ValCall(AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
OutputCall=7Output= 5 Qui si alloca un nuovo indirizzo per la variabile che prima aveva indirizzo AnInt. L’effetto della assegnazione XP:=7 è ristretto solo agli scopi della procedura ValCall.Ora XP e AnIntP rappresentano due diverse variabili puntatore, quando usciamo dalla procedura AnIntP^ vale sempre 5, mentre il valore di XP^ è perduto come spazzatura.
BEGINnew(AnIntP);AnIntP^:=5;VarCall (AnIntP);writeln(‘Output= ‘,AnIntp^:1);END.
OutputCall=7Output= 7 L’istruzione new(XP) fa sì che AnIntP diventa spazzatura. Quindi il valore 7 è mostrato due volte poiché tramite la Call VAR restituisce detto valore a AnIntp^.
ESERCIZIO E-MAIL
PROGRAM MESSAGGI;USES Stringa;VAR strDate, strEmail: stringADT;
InFile:text;MsgNum:integer;
PROCEDURE InitString (VAR strDate,strEMail:stringADT); {Inizializza le stringhe di inzio Data ed inizio Indirizzo}BEGIN END;
{Visualizza l'indirizzo elettronico e le date usando la ricorsione}PROCEDURE ExtractEMail_Date_Ric (VAR MsgNum: integer; VAR InFile:text);VAR
outstr,strApp,Date,EMail:stringADT;BEGIN
IF not eof(InFile) THENBEGIN
ReadlnString(OutStr,InFile);StrExtract(OutStr,1,strDate.len,strApp);IF strEqual(strApp,strDate) THENBEGIN
MsgNum:=MsgNum+1;StrExtract(outstr,strDate.len+1,outstr.len,Date);ReadlnString(outstr,InFile);StrExtract(outstr,strEMail.len+1,outstr.len,EMail);
END; ExtractEMail_Date_Ric(MsgNum,InFile);IF strEqual(strApp,strDate) THEN
BEGINPrintString(Date);write(' ');PrintString(EMail);writeln;
END;
END;END;PROCEDURE Introduction;BEGIN END;
{ BODY }
BEGIN InitString(strDate,strEMail); MsgNum:=0; Introduction; assign(InFile,'c:\tp\esempi\posta2.txt'); reset(InFile);
writeln('Visualizzazione con la Ricorsione');writeln(' Data Indirizzo');MsgNum:=0;reset(InFile);ExtractEmail_Date_Ric(MsgNum,InFile);close(InFile);writeln('Sono stati trovati ',MsgNum,' messaggi.');
readlnEND.
PROGRAM leggi_file_di_posta;uses strinsur;{ estrae da un file di posta elettronica gli indirizzi e le date e li stampa in ordine crescente per data }VARtesto:text; { file}righe:integer; { contatore di righe stampate }PROCEDURE elaboratesto(var testo:text;var righe:integer);{ procedura ricorsiva che elabora il testo } VAR from,ext,str1,date,ind:tipostringa; { stringhe } BEGIN converti('From -',from); { crea una stringa di controllo } inizializza(ext); inizializza(str1); inizializza(date); inizializza(ind); WHILE NOT(eof(testo)) AND (not(strequal(ext,from))) DO {estrae da ogni rigo una stringa e la confronta con quella di controllo }
BEGIN readlnstring(str1,testo); strextract(str1,1,6,ext); END;
IF strequal(ext,from) THEN BEGIN strextract(str1,8,50,date); { estrae la data } readlnstring(str1,testo); strextract(str1,14,50,ind); { estrae l'indirizzo }
elaboratesto(testo,righe); write(' C''Š un messaggio da parte di '); { stampa l'indirizzo e la data letti } writelnstringa(ind,output); write(' ricevuto in data = '); writelnstringa(date,output); writeln;
righe:=righe+1; { incrementa il contatore che permette di stampare 7 messaggi alla volta } IF (righe mod 7)=0 THEN BEGIN writeln('Premi un tasto per continuare '); readln;
END; END; END;
{ BODY }
BEGIN
assign(testo,'c:\tp\esempi\posta.txt');reset(testo);righe:=0;elaboratesto(testo,righe);write('Non ci sono pi— messaggi ');readln;
END.
Procedura B
ExtractEMail_Date_Ric(InFileIF not eof(InFile) THEN BEGIN
leggi(testo)estrai(strApp)IF strDate = ‘FROM’ THEN
BEGINestrai(data)leggi(testo)estrai(indirizzo)END
ExtractEMail_Date_Ric(InFile);fai_le_stampe
END
NOT(eof) strData =
FROMLeggiestrai
Fine Ricorsione
pop
Ricorsionepush
Leggiestrai
Stampa
SI
NO SI
NO
Procedura Aelaboratesto (testo)inizializzaStringhe(ext, str1, data, ind)WHILE NOT eof(testo) AND NOT ext=‘FROM’ DO {estrae da ogni rigo una stringa e la confronta con quella di controllo }
leggi(testo)estrai(ext)IF ext=‘FROM’ THEN
BEGIN estrai(data)
leggi(testo)estrai(ind)elaboratesto(testo)
fai_le_stampeEND
ENDEND
NOT(eof) &ext= FORM
strData =FROM
Leggiestrai
Fine Ricorsione
pop
Ricorsionepush
Leggiestrai
Stampa
WHILE
NO
SI
NO
SI