Accesso ai File. 1 I dati e la memoria centrale. R. Gallo ITIS A. Volta. Mar, 2010



Documenti analoghi
4 3 4 = 4 x x x 10 0 aaa

SISTEMI DI NUMERAZIONE E CODICI

Gestione della memoria centrale

Laboratorio di Informatica di Base Archivi e Basi di Dati

Codifica: dal diagramma a blocchi al linguaggio C++

Introduzione alla programmazione in C

ISTITUTO TECNICO INDUSTRIALE STATALE LA GESTIONE DEI FILE DI TESTO IN C++

Strutturazione logica dei dati: i file

Convertitori numerici in Excel

Architettura dei calcolatori II parte Memorie

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

Dispense di Informatica per l ITG Valadier

I file di dati. Unità didattica D1 1

APPUNTI DI MATEMATICA LE FRAZIONI ALGEBRICHE ALESSANDRO BOCCONI

Il memory manager. Gestione della memoria centrale

Definire all'interno del codice un vettore di interi di dimensione DIM, es. int array[] = {1, 5, 2, 4, 8, 1, 1, 9, 11, 4, 12};

Introduzione al Linguaggio C

Laboratorio di Informatica

Software relazione. Software di base Software applicativo. Hardware. Bios. Sistema operativo. Programmi applicativi

Fasi di creazione di un programma

Appunti sulla Macchina di Turing. Macchina di Turing

Variabili e tipi di dato

Sistema operativo: Gestione della memoria


Sistemi Operativi IMPLEMENTAZIONE DEL FILE SYSTEM. D. Talia - UNICAL. Sistemi Operativi 9.1

File, Modifica, Visualizza, Strumenti, Messaggio

Il Software. Il software del PC. Il BIOS

LA TRASMISSIONE DELLE INFORMAZIONI QUARTA PARTE 1

Le variabili. Olga Scotti

Introduzione. Classificazione di Flynn... 2 Macchine a pipeline... 3 Macchine vettoriali e Array Processor... 4 Macchine MIMD... 6

12 - Introduzione alla Programmazione Orientata agli Oggetti (Object Oriented Programming OOP)

Materiali per il modulo 1 ECDL. Autore: M. Lanino

Corso di Informatica

Università degli Studi di Padova Dipartimento di Matematica. - Corso di Laurea in Informatica

Informazione analogica e digitale

Sistemi Operativi IMPLEMENTAZIONE DEL FILE SYSTEM. Implementazione del File System. Struttura del File System. Implementazione

Funzioni in C. Violetta Lonati

LUdeS Informatica 2 EXCEL. Seconda parte AA 2013/2014

Organizzazione della memoria

Sistemi Operativi. 5 Gestione della memoria

Automatizzare i compiti ripetitivi. I file batch. File batch (1) File batch (2) Visualizzazione (2) Visualizzazione

Laboratorio di Informatica

INFORMATICA - I puntatori Roberta Gerboni

file:///c:/formazione/photoshop-webmaster-uffici/doc/guida-winzip.htm Guida a Winzip

UNA LEZIONE SUI NUMERI PRIMI: NASCE LA RITABELLA

Università degli Studi di Cassino Corso di Fondamenti di Informatica Puntatori. Anno Accademico 2010/2011 Francesco Tortorella

10 - Programmare con gli Array

Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica. Programmazione I - corso B a.a prof.

Dispensa di Informatica I.1

Gestione dei File in C

Capitolo Silberschatz

risulta (x) = 1 se x < 0.

Il File System. Il file system

Parte 1. Vettori di bit - AA. 2012/13 1.1

Sistema operativo. Sommario. Sistema operativo...1 Browser...1. Convenzioni adottate

La struttura dati ad albero binario

ARCHITETTURA CALCOLATORI: Memoria di massa (o secondaria)

Determinare la grandezza della sottorete

Il file system. meccanismi di accesso e memorizzazione delle informazioni (programmi e dati) allocate. in memoria di massa

BOZZA. cin per la comunicazione dal dispositivo di input standard, la tastiera, al programma (stream di input standard)

Il Software e Il Sistema Operativo. Prof. Francesco Accarino IIS Altiero Spinelli A.S. 09/10

Esercizi su. Funzioni

GUIDA ALLA PROGRAMMAZIONE GRAFICA IN C

Istruzioni per l installazione del software per gli esami ICoNExam (Aggiornate al 15/01/2014)

[Dimensionare la pagina-creare le tabelle-formattare le tabelle-formattare la pagina

Architettura dei computer

Sistemi Web! per il turismo! - lezione 3 -

La memoria - generalità

Architettura hardware

MANUALE MOODLE STUDENTI. Accesso al Materiale Didattico

Introduzione alle tecnologie informatiche. Strumenti mentali per il futuro

Prestazioni CPU Corso di Calcolatori Elettronici A 2007/2008 Sito Web: Prof. G. Quarella prof@quarella.

Concetto di Funzione e Procedura METODI in Java

Informatica per le discipline umanistiche 2 lezione 14

Corso di Fondamenti di Informatica

Informatica 1 Lezione 1

Appunti di Sistemi Elettronici

Sistemi operativi. Esempi di sistemi operativi

Test di informatica QUALE TRA I DISPOSITIVI DI MEMORIA ELENCATI HA LA CAPACITÀ PIÙ ELEVATA? a) Floppy disk b) Cd-Rom c) DVD Risposta corretta:

Traccia di soluzione dell esercizio del 25/1/2005

IL SISTEMA OPERATIVO IL SISTEMA OPERATIVO INTERFACCE TESTUALI INTERFACCE TESTUALI FUNZIONI DEL SISTEMA OPERATIVO INTERFACCE GRAFICHE

PROVA INTRACORSO TRACCIA A Pagina 1 di 6

Il SOFTWARE DI BASE (o SOFTWARE DI SISTEMA)

Software di base. Corso di Fondamenti di Informatica

Il sistema di I/O. Hardware di I/O Interfacce di I/O Software di I/O. Introduzione

Prova di Laboratorio di Programmazione

Architettura di un calcolatore

CALCOLATORI ELETTRONICI A cura di Luca Orrù. Lezione n.7. Il moltiplicatore binario e il ciclo di base di una CPU

INFORMATICA 1 L. Mezzalira

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

Capitolo 3. L applicazione Java Diagrammi ER. 3.1 La finestra iniziale, il menu e la barra pulsanti

All interno del computer si possono individuare 5 componenti principali: SCHEDA MADRE. MICROPROCESSORE che contiene la CPU MEMORIA RAM MEMORIA ROM

Manuale Utente Albo Pretorio GA

CPU. Maurizio Palesi

Uso di base delle funzioni in Microsoft Excel

puntatori Lab. Calc. AA 2007/08 1

Analisi e diagramma di Pareto

SISTEMI DI NUMERAZIONE DECIMALE E BINARIO

Algoritmo. I dati su cui opera un'istruzione sono forniti all'algoritmo dall'esterno oppure sono il risultato di istruzioni eseguite precedentemente.

PROCEDURA INVENTARIO DI MAGAZZINO di FINE ESERCIZIO (dalla versione 3.2.0)

Transcript:

Accesso ai File R. Gallo ITIS A. Volta Mar, 2010 La variabile File é fondamentale per la comprensione di un linguaggio di programmazione. Dal punto di vista informatico una variabile le é una struttura dati che permette la gestione di dati su memoria di massa. La trattazione in questo artcicolo é tutt'altro che esaustiva. Vi sono tantissime fonti bibliograche e link su internet dove reperire altro materiale. Convenzioni tipograche 1 1 I dati e la memoria centrale Quando scriviamo un programma in un linguaggio di programmazione, sappiamo che questo é l'ultima fase di una procedura che comporta una serie di passi quali 1. l'analisi del problema 2. la stesura di una bozza di soluzione che mostra per sommi capi quali sono i passi da seguire 3. la rappresetnazione graca, per chi vuole farlo, tramite un diagramma a blocchi che da una idea piú dettagliata e precisa del problema 4. inne, la traduzione in codice di programmazione Durante il lavoro di programmazione, bisogna introdurre le variabili sulle quali il programma esegue i calcoli. Un linguaggio di programmazione di alto livello mette a disposizione un insieme di tipi con i quali cominciare a lavorare, come ad esempio int, char, oat, double. Per problemi piú complessi possiamo dichiarare delle strutture dati derivate composte da tipi primitivi (vettori, stringhe, strutture etc). E molto importante capire approfonditamente come gestire le variabili, perché queste andranno a nire 1 L'articolo è stato scritto con dimensione caratteri superiore alla norma e gli esercizi si è cercato di concentrarli in una sola pagina, per dare l'ooportunità di vederne per intero lo svolgimento e lasciare spazio all'alunno di apporre le proprie note 1

2 in memoria centrale cioé in RAM. La RAM in un computer é una risorsa sempre scarsa. Una delle caratteristiche principali della RAM é la sua velocitá. Per un programma poter caricare in RAM tutte le variabili di cui ha bisogno, sarebbe la soluzione migliore. Purtroppo, ci sono applicazioni cosí complesse dove questo non é possibile. 1.1 Le variabili Le variabili sono delle locazioni di memoria che contengono un valore. Questo valore per tipi predeniti come int e double sono dei numeri. Un computer tuttavia, non capisce il sistema decimale e si deve convertire il valore decimale (intero o reale) in sistema binario (esadecimale). Una locazione di memoria cioé una variabile, non é altro che una sequenza di bit. All'interno della RAM si trovano una sequenza di tante variabili una dietro l'altra. Per leggere o scrivere una variabile c'é bisogno di conoscere la sua posizione nella RAM, cioé il suo indirizzo, che in C/C + + sappiamo essere un puntatore. La RAM é una struttura lineare e con questo intendiamo che si sviluppa in una sola dimensione. La RAM é costruita con transistor e condensatori. Le diverse tecnologie costruttive portano a RAM di tipo diverso che sono impiegate in dispositivi diversi (PC desktop, Notebook, tel cellulari, game-station) utilizzano RAM diverse 1.1.1 Come si legge/scrive in RAM Le operazioni di lettura/scrittura in RAM sono cosí semplici che oramai nessuno presta piú attenzione. Eppure bisogna interpretarne bene il signicato. L'operazione di lettura/scrittura avviene tramite l'assegnazione, ovvero usando l'operatore =. Tuttavia il simbolo = non va trattato come in una equazione algebrica dove l'ugualianza sta a signicare che si devono eseguire calcoli opportuni perché il termine destro sia uguale al termine sinistro. L'operatore di assegnazione sarebbe piú corretto scriverlo in questo modo. Con questa operazione intendiamo che il valore della variabile che sta alla destra del simbolo, nisce nella variabile di sinistra e il valore che questa conteneva viene perso. Questa operazione praticamente é immediata e non ha senso misurarne il tempo, a meno di casi particolari che coinvolgono lo spostamento di grossi blocchi di dati da una zona all'altra della memoria. 2 I dati e le memorie di massa Le memorie di massa hanno il grosso vantaggio di essere molto grandi. Viceversa hanno una velocitá d'uso molto inferiore alla RAM. Esiste una relazione inversamente proporzionale tra velocitá e dimensione: la RAM é molto veloce, ma piccola; la memoria di massa é molto capiente, ma lenta. Questo gap di velocitá, da sempre, é stato oggetto di studio e sperimentazione da

Gestione File 3 parte dei tecnici hardware/software. La lentezza dei dispositivi di massa é dovuta alla loro tecnologia costruttiva. Una memoria di massa é formata da una parte meccanica e dalla elettronica di controllo che si interfaccia col PC. La parte meccanica é costituita da dischi o, no ad alcuni anni fa da nastri, il cui movimento é regolato dal principio di inerzia. Ci vuole molto tempo per metterli in moto e altrettanto per fermarli. Bisogna intenderci su cosa si intende per molto tempo. La lentezza delle memorie di massa é da rapportare alla velocitá delle RAM. Anche se un hard-disk ha una velocitá in media di 7-10ms (milli sec), non potrá mai competere con la velocitá della RAM che é dell'ordine di 10-20ns (nano sec). Per fare un esempio, durante un accesso a disco che dura 10ms, nello stesso tempo la RAM avrá fatto 10 7 operazioni 2.1 Anatomia di un Hard-Disk Concentriamo la nostra attenzione sulla periferica piú diusa come memoria di massa e cioé l'hard Disk. Un disco rigido é formato da uno o piú dischi, chiamati piatti, ricoperti da materiale ferro-magnetico. I piatti possono essere di una lega di alluminio oppure di materiale vetro-resinoso. A causa del riscaldamento a cui sono sottoposti i piatti, l'alluminio é stato abbandonato in quanto ad alte temperature puó deformarsi. Sul disco é possible scrivere su entrambe le facce. La lettura/scrittura avviene tramite delle testine magneto-resistive che volano sul piatto senza mai toccare la supercie. Questo é possibile grazie all'alta velocitá di rotazione dei dischi che crea un cuscinetto d'aria sul quale poggiano le testine. Se il disco é formato da piú piatti allora sono presenti piú testine, 2 per ciasun piatto. Le testine sono collegate a un braccio che si sposta a seconda del punto dove deve leggere/scrivere sui piatti. Il braccio con le testine formano il cosiddetto pettine. Il pettine si sposta rispetto ai piatti in modo tangenziale e non perpendicolare, questo per diminuire la dimensione di tutto il disco. L'informazione elementare che memorizza il disco é il bit. Una porzione della supercie ferro-magentica chiamata cella del bit, viene orientata in un verso o in un altro a seconda del verso della corrente che scorre nelle testine. L'orientamento della cella del bit corrisponde a memorizzare uno 0 o un 1. 2.1.1 Struttura logica di un Hard-Disk Terminata per sommi capi, la descrizione di come é composto un hard-disk veniamo alla parte che ci interessa di piú: il suo funzionamento logico, ovvero come il software congura e utilizza un hard-disk. La gura 1 mostra la struttura logica dell'hd dopo una formattazione. La supercie di ogni piatto viene suddivisa in cerchi concentrici chiamate tracce, ogni traccia é a sua volta suddivisa in settori; la dimensione di un settore puó variare.

4 La posizione di una testina rispetto ai piatti, e quindi di tutte le altre del pettine, determina il cilindo. Nel paragrafo 1.1, abbiamo descritto i passi per leggere/scrivere una variabile in RAM. La struttura lineare della RAM rende questo processo molto semplice. Allo stesso modo se vogliamo leggere/scrivere i dati su un hard-disk, ci accorgiamo subito della complessitá del procedimento. Un hard-disk possiede una struttura a tre dimensioni. Per poter indirizzare un dato su un hard-disk dobbiamo fornire tre valori: cilindro-traccia-settore: il cilindro indica la testina in uso, quindi il piatto e la relativa faccia, dopo di che la traccia e il settore individuano il dato sul piatto scelto. E' importante capire la dierenza tecnologica che esiste fra RAM e HD perché questa ha notevole inuenza sull'uso dei le in un programma. 2.1.2 Come si legge/scrive su HD Quando si deve leggere/scrivere su un dispositivo é necessario fornire un indirizzo per recuperare la posizione dove sono presenti i dati. Per una struttura lineare a una sola dimensione come la RAM, costruire questo indirizzo é molto semplice e ci consente inoltre di puntare direttamente il singolo byte. La dimensione minima della cella indirizzabile in RAM é il byte. Se volessimo riprodurre lo stesso procedimento per un HD, le cose si complicano notevolmente. Nel paragrafo 2.1.1 abbiamo visto che i dati sono distribuiti sul disco, secondo una struttura a tre dimensioni cilindro-tracciasettore. Tutti i dati su disco sono memorizzati su unitá minime che sono i settori. Mentre per la RAM l'unitá minima di memorizzazione é il byte, per un HD l'unitá minima é il settore e la dimesione di un settore, purtroppo non é il byte. Quando si fanno operazioni di lettura/scrittura da disco viene letto/scritto un intero settore. La dimensione di un settore é fornita come parametro nel comando di formattazione e ha molta inuenza sulle performance dell'hd e della gestione dei le. La dimensione di un settore puó essere 512, 1024, 2048, 4096 byte o piú alta e dipende dal le system implementato nel sistema operativo. Proprio per questo motivo, storicamente un HD viene chiamato anche unitá a blocchi. 2.1.3 Inuenza della dimensione del settore sulle performance Un programma ben scritto che usa i le deve fare il minor numero di accessi al disco. Questa regola, seguita da tutti i programmatori, ha portato alla scrittura di una serie di algoritmi che minimizzano l'uso del disco. Alcuni di questi algoritmi sono cosí importanti da essere incorporati nel le system del sistema operativo. In alcuni casi queste tecniche sono addirittura implementate in hardware. Una variabile che inuisce sulla performance del disco é la dimensione del settore, che puó essere modicata dal programmatore. La scelta della dimensione di un settore é estremamente im-

Gestione File 5 Figura 1: Struttura logica HD portante per le performance dell'unitá disco nel sistema. Di seguito faremo alcuni esempi dove si evidenzierá come il valore di 512 byte non é il migliore da usare, come facilmente si potrebbe pensare.

6 File Generici Per cominciare, consideriamo la lettura/scrittura di un le qualsiasi. La dimensione di un le non coincide quasi mai con la dimensione di un settore o un multiplo di questo. Il le é presente in RAM prima di essere scaricato su disco. Per scrivere il le, il sistema operativo si occupa di eettuare la conversione da una struttura lineare a una dimensione come la RAM, in coordinate disco a tre dimensioni. Il le viene quindi suddiviso nella unitá minima di memorizzazione cioé il settore e sará distribuito su disco occupando i settori necessari. Per esempio un le di 11534 byte (pari a 11524/1024 = 11.25 kbyte) scritto su un disco formattato con un settore di 1024 byte occuperá 11 settori e una parte del dodicesimo. Se il disco é formattato con settori da 4096 byte occuperá 11534/4096 = 2.8 settori. Il sistema registra il le nello spazio disponibile che trova, se possibile scrivendo in settori contigui. Se non esiste spazio contiguo, il le viene distribuito in settori non vicini. In ogni caso il disco presenterá una frammentazione che alla ne inuenza notevolmente le prestazioni File di grandi dimensioni Un altro caso interessante riguarda i le di grosse dimensioni, questi le si incontrano molto spesso in computer graca o applicazioni multimediali. Quando si usano le di questo tipo, la performance del disco diventano prioritarie rispetto a quelle delle altre periferiche. Poiché si é detto che l'accesso a disco avviene leggendo/scrivendo un intero settore, per questi casi la dimensione del settore conviene assegnarla a un valore molto alto (8/16kbyte). Questi le non sorono del problema della contiguitá mentre permane quello della frammentazione Partizionamento del disco Se si é sicuri del tipo di le che si usa, la prima operazione da fare consiste nel formattare il disco con la dimensione del settore corretta. Se invece come succede nella maggior parte dei casi, ci sono le grandi e piccoli, il sistema operativo consente anche di partizionare un disco in modo da creare dei dischi con dimensione piú piccola. Le partizioni sono trattate come se fossero dei dischi indipendenti. Ogni partizione puó essere formattata con le system e dimensione dei settori diversi.

Gestione File 7 2.1.4 Misure di performance Rispetto a una operazione di lettura/scrittura eseguita in RAM (si veda 1.1.1), un accesso a disco comporta dei costi in termini di tempo. Per avere idea di quanto la nostra periferica si comporti ecientemente, ci sono alcuni parametri da misurare per calcolare la velocitá del disco. Questi parametri sono legati, ovviamente, alla presenza di parti meccaniche che come detto in 2.1, sottostanno al principio di inerzia quando si muovono. Ci sono tre parametri importanti di cui tener conto 1. tempo di ricerca: tempo che impiega la testina (pettine) a spostarsi sul piatto e trovare la traccia 2. tempo di latenza: tempo che intercore tra il posizionamento della testina sulla traccia e il passaggio del settore sotto la testina, mentre il disco ruota 3. tempo di read/write: tempo imiegato a leggere scrivere settore La somma di questi valori ci da un'idea del tempo di accesso a disco. Come si vede questi parametri non sono inuenzati, se non in minima parte, dal software scritto dal programmatore, in quanto sono valori legati alla tecnologia costruttiva. Esistono oggi in commercio periferiche con velocitá di rotazione dei dischi attorno ai 15000giri/min e bracci delle testine molto leggeri per spostarsi con piú facilitá sui piatti. Dove si puó agire invece, é sulla corretta organizzazione dei le sui disco, secondo le indicazioni date in 2.1.3 2.1.5 Conclusioni Al termine di questa esposizione del funzionamento di un hard-disk ci si pone la domanda: come usare queste informazioni per scrivere un programma corretto e soprattutto eciente? A questa domanda risponderemo man mano che saranno sviluppati gli esempi. 3 La variabile File Nella pratica comune di tutti i giorni, un le é un termine che usiamo per identicare un elemento che sta su un dispositivo chiamato memoria di massa e che contiene dei dati utili al nostro lavoro. Questi dati sono persistenti nché qualcuno non cancella il le. Per quanto riguarda la denizione che si da in informatica, le cose cambiano in modo sostanziale, perché, per chi scrive un programma in un qualunque linguaggio di programmazione, la gestione di un le comporta la conoscenza, come abbiamo visto, di vari argomenti non solo legati al linguaggio, ma anche al sistema operativo e all'hardware del disco. Questo articolo illustra l'uso di un le a prescindere dal linguaggio e

8 dal sistema operativo. Gli esempi di codice presenti sono scritti in C perché il linguaggio ore maggiore essibilitá e permette di comprendere alcuni aspetti dell'argomento in maggiore dettaglio. 3.1 Strutture dati La scrittura di un programma in un linguaggio di programmazione imperativo, richiede che in testa del codice vengano dichiarate le variabile sulle quali il programma dovrá lavorare. L'obbligo della scrittura delle variabili non é una scelta dei progettisti del linguaggio che impongono di scrivere l'elenco delle variabili da usare prima del codice, ma una necessitá imposta dall'hardware. Un programma é un oggetto che quando va in esecusione, consuma delle risorse hardware che sono assegante al programma dal sistema operativo. Queste risorse sono, almeno per programmi semplici, la CPU e la RAM. Altri programmi piú complessi, hanno bisogno di accedere alle periferiche come i dischi rigidi, il monitor e la stampante, tanto per citarne alcune. Il codice di un programma deve essere compilato e linkato per arrivare nella forma di eseguibile. Nella fase di compilazione il codice sorgente viene tradotto in linguaggio oggetto. Questa forma intermedia rappresenta il codice sorgente tradotto in una forma numerica non ancora eseguibile. Il codice compilato contiene le informazioni relative all'occupazione della memoria, cioé quanta memoria il programma occuperá quando entra nello stato di esecuzione e diventa un processo. Quando si scrive un programma siamo abituati a elencare le variabili necessarie per risolvere il problema e quindi possiamo cancolare quanti byte occupano le variabili quando saranno allocate in memoria centrale. 3.1.1 Esempi allocazione memoria 1. Se vogliamo risolvere il problema del calcolo della media delle temperature delle cittá dei capoluoghi di regione in Italia, dichiariamo un vettore di 20 elementi di tipo double, un valore per ciascun capoluogo. Lo spazio di memoria occupato sará di 80 byte ovvero 280 = 20*8 byte, visto che un double é grande 8 byte. 2. Consideriamo il problema di registrare i voti in matematica di tutti gli alunni di una scuola come L'ITIS Volta. La popolazione della scuola é composta da circa 450 alunni, dichiareremo allora un vettore di 450 elementi di tipo unsigned int per una occupazione di memroia di 1800=450*4 byte. 3. La registrazione dei valori di temperatura all'interno di un forno effettuata ogni secondo durante una giornata, porta a dover dichiarare un vettore di 86400=60(sec)*60(min)*24(ore) posizioni che occupano 691200=86400*8byte se esprimiamo la temperatura in double.

Gestione File 9 4. Altro problema interessante consiste nel ripetere la procedura del problema precedente per 30gg Come si vede negli esempi precedenti, la dimensione della struttura dati per contenere i valori, aumenta sempre di piú, sino ad arrivare al punto che l'esempio 3 e 4 non possono essere risolti con l'uso di un vettore. Non é possibile usare un vettore in quanto ci sono vincoli hardware: un vettore non puó essere allocato in ram se supera una certa dimensione. In particolare per l'esempio 4 non si puó usare neanche la ram, ma scrivere i dati su altro dispositivo. 3.2 Variabile File Nella paragrafo 3.1, abbiamo fatto alcuni esempi dove, in generale, é possibile conoscere quanti byte occupano le strutture dati in memoria centrale, allocate per un dato programma. La quantitá di memoria l'abbiamo calcolata prima di mandare in esecuzione il programma. In questo caso stiamo parlando di allocazione statica della memoria a tempo di compilazione. Il caso dell'esempio 4 peró non é del tutto risolto. Cosa succederebbe se la registrazione delle temperature deve proseguire per 2,3,4 o piú mesi. Non sappiamo di quanta ram abbiamo bisogno per registrare tutti i valori, ovvero il programma non conosce prima di andare in esecuzione di quanta memoria avrá bisogno per fare i calcoli. Siamo in presenza di un programma che alloca lo spazio di memoria solo a run-time ovvero di allocazione dinamica della memoria. E se la memoria presente nel PC non fosse suciente, a tempo di esecuzione, per registrare tutti i dati, dove potremmo scriverli? Per risolvere questi problemi si deve ricorrere all'uso di un'altra periferica, oltre la RAM, e cioé di un dispositivo di memoria di massa. Nel nostro caso faremo riferimento per semplicitá al disco rigido. In un linguaggio di programmazione quando parliamo di le usiamo una de- nizione piuttosto strana. Una variabile le é una variabile con un tempo di vita superiore a quella del programma. Torneremo in seguito su questa denizione. Per il momento cominciamo ad analizzare esempi di codice. Il linguaggio usato come detto é il C/C + +

10 3.2.1 File di numeri - Scrittura In questo esempio si crea un le di numeri. La classe del C/C + + che tratta i le si chiama fstream che si suddivide in ifstream se trattiamo solo le in input e ofstream se trattiamo solo le in output int main(int argc, char* argv[]) { ofstream ofs; // stream in sola scrittura int num; char str[1]; long int position; // attributo ios::out della classe ofstream indica file in scrittura ofs.open("c:\\scuola1.dat", ios::out); if (!(ofs)) // se il sistema operativo non riesce a aprire/creare file cout << "errore su apertura file in ingresso"; else { // altrimenti crea il file e restituisce un puntatore in ofs do { // l'operatore << e' polimorfico, // ora scrive su monitor che è un file con nome cout cout << "Numero... "; // l'operatore >> e' polimorfico, // ora legge dalla tasitera che è un file con nome cin cin >> num; // ora scrive sul file ofs num ofs << num << '\n'; cout << "Hai finito [s/n]?"; cin >> str; while ((strcmp(str,"s"))); // continua se l'espressione e' vera ofs.close(); // i file vanno sempre chiusi return 0; Il programma scrive su uno stream di output tanti numeri nché non premo il tasto 's'. Il nome del le viene dato nel metodo open. Il nome del le é formato dal path e dal nome vero e proprio. In questo caso si scrive nel directory c: ma un utente normale che si logga in windows o linux, non ha l'accesso alla cartella principale, quindi il path va sostituito con la cartella /documenti (in windows) o /home (in linux).

Gestione File 11 3.2.2 File di numeri - Lettura Il codice seguente legge un le di numeri cosí come scritto in 3.2.1 int main(int argc, char* argv[]) { ifstream ifs; int num; long int position; ifs.open("c:\\scuola1.dat", ios::in); if (!(ifs)) cout << "Errore apertura file in uscita"; else { // mentre non siamo arrivati alla fine file EOF?? while (ifs.good()) { // legge il numero dal file e lo stampa ifs >> num; cout << "Numero... " << num; // stampa anche la posizione del num nel file position = ifs.tellg(); cout << " alla posizione -> " << position << endl; getch(); // chiude il file ifs.close(); return 0;

12 3.2.3 File di stringhe - Scrittura In questo esempio si scrive un le di stringhe int main(int argc, char* argv[]) { ofstream ofs; char s[1], str[100]; ofs.open("c:\\scuola2.dat", ios::out); if (!(ofs)) cout << "Errore apertura fiel in uscita"; else { do { cout << "Stringa... "; cin >> str; ofs << str << '\n'; cout << "Hai finito [s/n]?"; cin >> s; while (strcmp(s, "s")); ofs.close(); return 0;

Gestione File 13 3.2.4 File di stringhe - Lettura In questo esempio si legge il le scritto in 3.2.3 int main(int argc, char* argv[]) { ifstream ifs; char str[100]; ifs.open("c:\\scuola2.dat", ios::in); if (!(ifs)) cout << "Errore apertura file in uscita"; else { while (ifs.good()) { // getline legge massio 100 char oppure quando torva fine stringa ifs.getline(str,100); cout << "Stringa... " << str << endl; cout << "-> Premi un tasto per finire " << endl; getch(); ifs.close(); return 0; 3.3 Osservazioni 3.3.1 Polimorsmo Negli esempi dei paragra precedenti abbiamo fatto uso delle proprietá della programmazione orientata agli oggetti OOP. La scrittura/lettura dei dati avviene con operatore polimorco. Il polimorsmo consente di eseguire una operazione senza preoccuparci del tipo di dato impiegato. Fermiamoci un attimo. Finora non abbiamo accennato al fatto che scrivere su un le un numero o una carattere non é la stessa cosa. Se avessimo scritto gli esempi in C avremmo avuto problemi. In C esiste la funzione read/write per leggere/scrivere, ma ha bisogno di conoscere il tipo di dato che si legge/scrive. 3.3.2 Istanza di un oggetto Per usare un le si deve istanziare un oggetto dalla classe fstream (ifstream, ofstream) che sta nel le include fstream.h. Il metodo open dell'oggetto istanziato apre/crea un le a seconda del valore dell'attributo ios. Il metodo open riceve un parametro che é il nome del le

14 4 Lettura/Scrittura le generici Negli esempi precedenti abbiamo trattato le esclusivamente di numeri, o esclusivamente di caratteri. Chiediamoci ora cosa fare quando usiamo le di struct. Un le di strutture é la condizione piú frequente che si ritrova nel lavoro quotidiano. Viste le considerazioni dei paragra precedenti, il problema che sorge é quello di trasferire i dati da una struttura lineare come la RAM, su una memoria di massa con una tecnologia hardware diversa, le cui variabile sono accessibili tramite 3 variabili: cilindro, traccia, settore. 4.1 File Binari Negli esempi 3.2.1 e 3.2.3, la variabile da scrivere occupa in RAM sempre lo stesso spazio, o un multiplo di questo. Per un intero 4 byte, per un vettore di interi n*4 byte se il vettore é formato da n elementi. Trasferire questa struttura su le risulta semplice. Un rapido calcolo ci dice quanti settori su disco occuprará la nostra variabile. Di seguito alcuni esempi per un vettore di 1000 elementi di interi pari a 4000 byte, lo spazio occupato su disco sará di 1 settore, se come dimensione del settore si é scelta, durante la formattazione, un valore di 4096. Numero di accessi per recuparare tutto il vettore é 1 per lo stesso vettore, i settori occupatio saranno 4, di cui uno occuapto parzialmente, se la dimensione del settore scelta é pari a 1024 byte. Si corre il rischio di avere un le frammentato con notevole diminuizione del tempo di accesso da parte delle testine su disco, in quanto queste si spostano sulla supercie per recuperare tutto il vettore Una variabile struct ci pone davanti un problema, se vogliamo piú complesso. La struct contiene var di tipo diverso e dimensione diversa: lo spazio occupato dalla variabile in RAM é la somma dei byte di ogni singola variabile della struttura. La stessa variabile su disco occuperá un numero di settori in base alla dimensione scelta in fase di formattazione, ma in fase di lettura/scrittura un ulteriore passaggio é necessario, anché i dati vengano recuperati in memoria secondo la loro dimensione e anche rispettando la stessa posizione occupata nella struttura. Piú avanti si vedrá l'esempio di una struttura particolare, trasformata secondo il meccanismo del cast che porta risultati molto interessanti. Quest'ultimo esempio é possibile grazie alla essibilitá del linguaggio C.

Gestione File 15 4.1.1 Scrittura le binario Viene presentato il brano di codice che si occupa della scrittura di un le binario, ossia di una struttura generica struct blocco { char nome[100]; char cognome[100]; char indirizzo[100]; unsigned int stipendio; unsigned short int eta; bl; int main (int arcg, char *argv[]) { char *pbl; // crea e apre il file binario ofstream ofs("c:\\blocco.dat"); do { cout << "Nome... "; cin.getline(bl.nome,100); cout << "Cognome... "; cin.getline(bl.cognome,100); cout << "Indirizzo... "; cin.getline(bl.indirizzo,100); cout << "Stipendio... "; cin >> bl.stipendio; cout << "Eta... "; cin >> bl.eta; cout << endl << "Hai finito [s/n]??"; cin >> s; // scrive il blocco COMPOSTO DA STRINGHE E DA NUMERI pbl = (char *)&bl; ofs.write(pbl, sizeof(bl)); while (s!="s"); ofs.close(); return 0; Nel brano di codice precedente, nel pieno rispetto dello stile C, il le viene creato e successivamente riempito con i dati della struttura. A dierenza degli esempi che scrivono/leggono numeri o stringhe, non é stato creato un operatore polimorco come o. Invece si usa il metodo write che scrive una sequenza di byte. Prima dell'istruzione write viene eettuato un cast

16 che va letto in questo modo: preso il puntatore al blocco bl lo si trasforma in un puntatore a char, ovvero un puntatore a una sequenza di byte. Poiché la struttura é composta da tipi dierenti, risulta dicile trattare la scrittura/lettura su le con operatore polimorco, si decide allora di trasformare tutto in una sequenza di byte: come se la struttura passasse attraverso un tritacarne che scompone in pezzi piú piccoli le varie parti di cui é composta. In ultimo, per ottenere la massima essibilitá si usano i puntatori. Il metodo write richiede un altro parametro cioé la dimensione della stringa da scrivere. Lascio al lettore spiegarne il motivo. 4.1.2 Lettura le binario Il codice seguente mostra come fare la lettura di un le binario usando sempre la stessa struttura int main (int arcg, char *argv[]) { // crea e apre il file binario ifstream ifs("c:\\blocco.dat"); while (ifs.good()) { ifs.read((char*)&bl, sizeof(bl)); cout << bl.cognome << endl; cout << bl.nome << endl; cout << bl.indirizzo << endl; cout << bl.stipendio << endl; cout << bl.eta << endl; ifs.close(); return 0; dove il metodo read carica in mmemoria i dati da inserire nella struttura. Il metodo si aspetta di trovare una struttura in memoria centrale, che corrisponda sia in dimensione sia in posizione. Il rischio é quello di usare una struttura dati che corrisponde in grandezza a quella presente su disco ma con campi disposti in modo diverso o di tipo dierente ma che hanno la stessa dimensione. Il metodo eettua il cast sui campi letti, con le conseguenze che possiamo immaginare.

Gestione File 17 4.1.3 Esercizio Si considerino le due struttura seguenti struct ST1 int n; char c[4]; float f; st1; struct ST2 { char c[4]; float f; int n; Si scriva un programma che crei/sriva/legga da le creando il le prima con la ST1 e leggendo con ST2. Quali sono i risultati? 4.1.4 Proprietà sequenziale di un le Una variabile le indirizza un'insieme di elementi di tipo sequenziale. Un insieme possiede questa proprietá quando tra gli stessi elementi si stabilisce una relazione d'ordine. Per esempio l'insieme dei numeri Interi é sequenziale. Un elemento dell'insieme é preceduto e seguito da un altro elemento in relazione d'ordine di essere il maggiore e il minore dell'elemento precedente e dell'elemento successivo. Oltre a questa proprietá ne sono denite altre. La relazione d'ordine ci dice che un solo elemento per volta puó essere trattato. La posizione corrente dell'elemento viene espressa come se l'insieme fosse composto da due parti: la parte sinistra e la parte destra dell'elemento considerato. Nel caso specico dell'insieme le, l'elemento generico prende il nome di registrazione. La gura 1 suggerisce che é possibile recuperare una registrazione del le secondo le proprietá che abbiamo prima enunciato, ovvero accedere alla registrazione, senza scorrere le registrazioni precedenti presenti nel le. Il recupero diretto della registrazione in una struttura lineare (RAM) é una operazione molto semplice. Viceversa, la stessa operazione su memoria di massa comporta una operazione di traduzione di indirizzi, delle quali si occupa il driver del sistema operativo che comanda il controller della periferica. Queste operazioni sono trasparenti al programmatore, che opera a livello di astrazione del linguaggio di programmazione. Il C/C + +, come altri linguaggi, fornisce una serie di istruzioni, per il recupero della posizione della registrazione.

18 4.1.5 Accesso a File Lo stream standard di accesso alle periferiche, possiede le istruzioni tellp[ut] seekp[ut] tellg[et] seekg[et] per recuperare e accedere alla posizione della registrazione. Il susso p sta per put nel caso il usso sia aperto in scrittura, mentre il susso g sta per get se il usso é aperto in lettura. I metodi tell/seek possiedono a loro volta i seguenti attributi ::beg ::cur ::end per indicare che il recupero della posizione cercata della registrazione, va calcolata a partire dall'inizio del le, dalla ne del le e dalla posizione corrente del le

Gestione File 19 4.1.6 Esempi uso tell/seek Recupera la registrazione x del le Si ipotizza la dichiarazione della struttura BLOCCO in le header o altra parte int main (int arcg, char *argv[]) { long int x; char *pbl; BLOCCO bl; // crea e apre il file binario ifstream ifs("c:\\blocco.dat"); cout << ``Quale registrazione recuperare dal file... ``; cin >> x; // il valore di x va moltiplicato per la dimensione in byte // del blocco che compone la registrazione, altrimenti // si recupera il byte che occupa la posizione x // recupera la posizione cercata dall'inizio del file ifs.seekg(x*sizeof(bl), ios::beg); ifs.read((char*)&bl, sizeof(bl)); cout << bl.cognome << endl; cout << bl.nome << endl; cout << bl.indirizzo << endl; cout << bl.stipendio << endl; cout << bl.eta << endl; ifs.close(); return 0;

20 Funzione FileSize In questo esempio si scrive sotto forma di funzione, l'istruzione FileSize che in C/C + + non é presente, mentre é fornita in altri linguaggi. Nell'esempio il le é binario long int FileSize (ofstream &file) { long int start, end, sizefile; unsigned int uiblocchi; BLOCCO bl; // crea e apre il file binario ifstream ifs("c:\\blocco.dat"); ifs.seekg (0, ios::beg); // porta il puntatore del file all'inizio start = ifs.tellg(); // recupera la prima posizione ifs.seekg (0, ios::end); // porta il puntatore del file alla fine end = ifs.tellg(); // recupera l'ultima posizione sizefile = (end - start); // dimensione file in byte uiblocchi = (end - start) / sizeof(blocco); // num di blocchi ifs.close(); return sizefile;