Corso di Visual Basic
(Parte 11)

 

capoletteraQ.jpg (6397 byte)uesto mese il corso di Visual Basic focalizza l’attenzione sui file ad accesso casuale, che si distinguono per essere dotati di una struttura rigida; ciò li rende in grado di offrire una maggiore velocità di accesso alle informazioni

Nella scorsa lezione sono stati presi in considerazione i file di testo, in cui l’accesso alle informazioni avviene in modo sequenziale. Essi presentano un limite, ovvero la scarsa idoneità alla memorizzazione di grandi quantità di dati, a causa dei talvolta elevati tempi di ricerca.
In queste condizioni sono spesso preferibili delle strutture più sofisticate, quali ad esempio i file ad accesso casuale, che sono oggetto di studio in questa puntata del corso.
Come sempre, tuttavia, prima di affrontare i nuovi argomenti, è opportuno un ripasso dei concetti esposti in precedenza; lo spunto è fornito dalla correzione dell’esercizio proposto nella scorsa lezione.

La soluzione dell’esercizio proposto nella scorsa lezione

L’esercizio proposto nello scorso numero prevede la realizzazione di un programma in grado di leggere, per mezzo di opportune textbox, tre valori numerici interi e una stringa, nonché di offrire la possibilità di salvare questi dati su richiesta dell’utente. La pressione di un pulsante deve provocare il ripristino all’interno delle caselle di testo dei valori salvati. Il codice è contenuto nel Listato 1. L’applicazione è composta da due procedure; la prima, associata alla pressione del pulsante btnSalva, ha lo scopo di scrivere le informazioni sul file Archivio.dat.
Si noti che è copiata sul disco solo la parte intera dei numeri; l’eventuale parte decimale, non prevista dall’esercizio, è troncata. I dati sono letti dalla procedura associata alla pressione del pulsante btnCarica, che fa uso dell’istruzione Line Input.

I file ad accesso casuale

I file ad accesso casuale sono caratterizzati dall’organizzazione molto rigida in strutture dette record. Un file è quindi composto da un numero variabile di record accodati. Al fine di comprendere meglio il concetto, si pensi a uno schedario, quale ad esempio l’anagrafica dei clienti di un’azienda. L’archivio è composto da schede aventi tutte la stessa dimensione e contenenti lo stesso tipo di informazione. Ogni scheda rappresenta un record.

I record

Per poter inserire in un file più informazioni, anche di tipo non omogeneo, raggruppate in "schede", è necessario riunirle in un’unica struttura. Si deve quindi creare un nuovo tipo di dati. Tale operazione è eseguita per mezzo della struttura Type, la cui sintassi è:

Type <nome>
<nome_campo_1> As <tipo_1>
<nome_campo_2> As <tipo_2>

<nome_campo_n> As <tipo_n>
End Type

All’interno della struttura devono essere dichiarati gli elementi che la compongono, che sono denominati campi. Per ognuno di essi deve essere specificato il tipo di dati che lo caratterizza. I campi possono essere anche di tipo totalmente diverso. Si possono infatti definire dei record contenenti contemporaneamente delle informazioni di tipo testuale, numerico e logico. Ad esempio, la seguente dichiarazione è corretta:

Type Automobile
Marca As String*50
Modello As String*50
Cilindrata As Integer
Diesel As Boolean
End Type

Si noti che sono stati dichiarati due campi di tipo alfanumerico, uno di tipo logico e uno numerico intero. La struttura è denominata Automobile. In questo modo si è provveduto ad aggiungere un nuovo tipo di dati a quelli standard previsti da Visual Basic. È quindi possibile dichiarare la variabile MiaVettura, di tipo Automobile digitando:

Dim MiaVettura As Automobile

Riempimento dei campi di un record

Le variabili definite come record prevedono, data la propria conformazione, una modalità di assegnamento dei valori leggermente diversa rispetto a quella prevista dalle strutture convenzionali. In genere, si esegue un’operazione di assegnamento per ogni campo, secondo la sintassi:

<variabile>.<campo> = <valore>

Ad esempio, volendo assegnare il valore 2499 al campo Cilindrata della variabile MiaVettura, definita in precedenza, occorre scrivere:

MiaVettura.Cilindrata = 2499

L’apertura di un file ad accesso casuale

Come per le strutture sequenziali, l’apertura di un file ad accesso casuale avviene per mezzo dell’istruzione Open che, in questo caso, assume la forma:

Open <percorso_file>
For Random
As [#]<identificatore>
Len = <lunghezza_record>

dove percorso_file indica il percorso completo del file che si desidera aprire, mentre identificatore costituisce un numero utilizzato per contraddistinguere in modo univoco tale struttura e pertanto va fornito come parametro a tutti i comandi che sono destinati a gestirla. Si noti che la modalità di accesso è indicata per mezzo della parola chiave Random, che deve essere obbligatoriamente specificata, indipendentemente dal tipo di operazione che si desidera effettuare sul file, sia essa di lettura o scrittura. Si noti altresì che anche il parametro Len è obbligatorio. Esso deve contenere l’esatta dimensione del record che costituisce l’unità di informazione memorizzata nel file. Qualora essa non sia nota, può essere determinata per mezzo della funzione Len. Ad esempio, volendo assegnare alla variabile DimRec la dimensione del record MiaVettura, è necessario digitare:

DimRec = Len(MiaVettura)

Il contenuto della variabile DimRec costituisce il valore da passare come ultimo parametro all’istruzione Open per consentire la gestione di un file composto da elementi di tipo Automobile. Analogamente ai file sequenziali, anche le strutture ad accesso casuale devono essere chiuse dopo l’uso per mezzo dell’istruzione Close.

La lettura di un record

La lettura di un record contenuto in un file ad accesso casuale avviene per mezzo dell’istruzione Get, caratterizzata dalla seguente sintassi:

Get [#]<identificatore>, <posizione>, <variabile>

dove <identificatore> rappresenta il numero che univocamente identifica il file oggetto dell’operazione di lettura e <variabile> è il nome della variabile in cui i dati letti devono essere posti. Il parametro <posizione> indica la posizione del record da leggere. Si tratta di un valore intero compreso fra 1 e il numero dei record presenti nel file. Ad esempio, si supponga di voler accedere al quarto record presente nel file di identificatore 1 e di voler porre il suo contenuto nella variabile Dato. Ciò è possibile per mezzo della riga:

Get #1, 4, Dato

Solo in un caso il valore del parametro <posizione> può essere omesso; ciò si verifica in occasione dell’effettuazione di operazioni di lettura in sequenza; l’assenza del numero indicante la posizione provoca infatti l’accesso al record successivo a quello corrente. Non possono tuttavia essere omesse le virgole di separazione. Ad esempio, la sequenza

Get #1, 4, Dato
Get #1,, Dato1

provoca la lettura del quarto e del quinto record del file identificato dal numero 1.

La scrittura di un record

Per scrivere il contenuto di un record in un file ad accesso casuale è possibile utilizzare l’istruzione Put, la cui sintassi è pressoché identica a quella del comando Get:

Put [#]<identificatore>, <posizione>, <variabile>

In questo caso, la variabile indicata come terzo parametro contiene il dato da scrivere. Ad esempio, la riga

Put #1, 5, Dato

scrive il contenuto della variabile Dato nel quinto elemento del file identificato dal numero 1. Il valore assunto dal parametro <posizione> assume un’importanza fondamentale, in quanto determina se è aggiunto un nuovo record all’archivio o se ne è sovrascritto uno già esistente. Quest’ultima evenienza si verifica quando è indicata una posizione già occupata da un elemento. Per aggiungere un nuovo record al file, invece, è necessario indicare un valore pari al numero totale dei record incrementato di un’unità.

La cancellazione logica di un record

Il metodo più semplice per cancellare un record consiste nel sovrascriverlo con un elemento vuoto. In questo modo, tuttavia, non è possibile recuperare lo spazio da esso occupato sul disco. Si tratta cioè di una cancellazione logica, non fisica, in quanto Visual Basic non dispone di un’istruzione in grado di rimuovere un record e di recuperare automaticamente lo spazio da esso occupato. È possibile sfruttare a proprio vantaggio la possibilità di effettuare solo una cancellazione logica dei record contenuti in un file per fare in modo che degli elementi eventualmente eliminati per sbaglio possano essere agevolmente recuperati. Ciò è possibile aggiungendo un campo booleano alla struttura dei record e facendo in modo che il programma che accede all’archivio consideri cancellati tutti gli elementi caratterizzati dal contenere il valore logico True all’interno di questo campo. L’eliminazione di un record comporta quindi la semplice variazione del valore di un suo campo. Analogamente, è possibile recuperare un elemento cancellato per errore impostando nuovamente al valore False il campo booleano. La struttura Automobile può pertanto essere modificata come segue:

Type Automobile1
Marca As String*50
Modello As String*50
Cilindrata As Integer
Diesel As Boolean
Cancellato As Boolean
End Type

La cancellazione fisica di un record

Quando la quantità di informazioni da gestire diventa elevata, la necessità di recuperare lo spazio occupato dai record cancellati diventa evidente, sia per evitare lo spreco di spazio sul disco, sia per non ridurre drasticamente i tempi di accesso alle informazioni costringendo il programma a leggere dei dati inutili. Come già osservato in precedenza, Visual Basic non dispone di un’istruzione in grado di provvedere automaticamente alla cancellazione fisica di un record. Tuttavia, la scrittura di una simile procedura non presenta un livello di difficoltà elevato. Essa deve solo creare un nuovo file e copiare al suo interno tutti i record non vuoti. Successivamente, deve eliminare il primo file ed assegnare il suo nome alla nuova struttura. È ciò che fa la procedura di seguito descritta, che riceve come parametro il nome del file da compattare, che si suppone composto da record di tipo Automobile1:

Sub CompattaFile(ByVal NomeFile As String)
Dim ID_old As Integer
Dim ID_new As Integer
Dim Dato As Automobile1
Dim Lunghezza As Integer
Lunghezza = Len(Dato)
ID_old = FreeFile
Open NomeFile For Random As ID_old Len = Lunghezza
ID_new = FreeFile
Open "Temp.dat" For Random As ID_new Len = Lunghezza
Do While Not EOF(ID_old)
Get ID_old, , Dato
If Not Dato.Cancellato Then
Put ID_new, , Dato
End If
Loop
Close ID_old, ID_new
Kill NomeFile
Name "Temp.dat" As NomeFile
End Sub

Si noti l’uso della funzione FreeFile, che restituisce un numero adatto ad essere utilizzato come identificatore di file ed evita così il rischio di utilizzare degli identificatori già in uso in altre parti del programma. La procedura provvede a leggere in modo sequenziale il file di cui è specificato il nome come parametro e a copiare in un file denominato Temp.dat tutti i record per i quali il campo Cancellato assume il valore False. Si noti che le operazioni di lettura e scrittura sono eseguite sequenzialmente, in quanto è stato omesso il valore indicante la posizione nelle istruzioni Get e Put. Il ciclo di copiatura termina quando sono esauriti i record da leggere. Quando ciò avviene, la funzione EOF (End Of File), già descritta nella scorsa lezione, restituisce il valore True. Dopo aver copiato tutti i record non cancellati logicamente, la procedura provvede a chiudere entrambi i file. Si noti che a tal fine utilizza un’unica istruzione Close, in cui gli identificatori dei file da chiudere sono separati da una virgola. Il passo successivo consiste nel sostituire il file originale con quello creato. Ciò comporta l’esecuzione di due operazioni: la cancellazione del file di origine e la ridenominazione di quello generato dalla procedura. L’eliminazione avviene per mezzo dell’istruzione Kill, la cui sintassi è

Kill <Nome_file>

Il file Temp.dat è quindi rinominato per mezzo dell’istruzione Name, che è caratterizzata dalla seguente sintassi:

Name <Vecchio_nome> As <Nuovo_nome>

Un esempio…

Il Listato 2 contiene il codice di un’applicazione in grado di archiviare i dati relativi a dei siti Internet. Per ognuno di essi, è possibile indicare l’indirizzo e una breve descrizione. È possibile creare dei nuovi record vuoti in grado di ospitare i dati relativi a nuovi siti, oppure modificare le informazioni riferite a quelli esistenti, nonché scorrere l’archivio nei due sensi.
Il file è composto da record del tipo DatiURL, definito all’inizio. Si noti che la definizione è preceduta dalla parola chiave Private per indicare che il suo campo di validità è limitato al form che la contiene. I record sono letti e scritti rispettivamente per mezzo delle procedure LeggiRecord e ScriviRecord. La variabile globale Posizione stabilisce il numero del record da leggere o aggiornare. Si noti la presenza della funzione ContaRecord, che ha lo scopo di calcolare il numero dei record presenti in archivio come rapporto fra la dimensione del file, restituita dalla funzione LOF (Length Of File) e la lunghezza di un singolo elemento.L’apertura del file avviene in corrispondenza dell’avvio dell’applicazione, quindi al verificarsi dell’evento Load. Analogamente, per assicurare la chiusura dell’archivio quando cessa l’uso del programma, l’istruzione Close è stata posta fra il codice associato all’evento Unload.

… e un esercizio

Per esercitarsi sui concetti esposti, si provi a modificare l’esempio sopra descritto aggiungendo la possibilità di cancellare logicamente e, su richiesta, fisicamente dei record.

Conclusioni

I file rivestono un’importanza fondamentale nella maggior parte delle applicazioni, in quanto consentono di trattare una quantità di dati superiore a quella che può essere contenuta nella memoria dell’elaboratore. Inoltre, trattandosi di strutture permanenti, permettono di mantenere tali informazioni anche dopo lo spegnimento o il riavvio del sistema. L’utilizzo di strutture ad accesso casuale rende ancora più evidenti i vantaggi offerti dai file, permettendo una maggiore flessibilità d’uso.

Maurizio Crespi si occupa principalmente di grafica e multimedia. Svolge la funzione di responsabile tecnico presso Datanord Multimedia, società specializzata nella realizzazione di software orientato al marketing e all’editoria, per la quale progetta e sviluppa applicazioni in C++, Visual Basic, Delphi e Director. Può essere contattato per e-mail come crespi@programmers.net.