Nelle
routine di gestione degli errori viste nella lezione precedente
abbiamo considerato alcuni errori specifici, che sapevamo
avrebbero potuto verificarsi durante l'esecuzione dell'applicazione;
ovviamente non si può prevedere tutto, pertanto è
necessario gestire anche gli errori non previsti.
Un modo banale, ma che ha il vantaggio di impedire la chiusura
inaspettata del programma, è quello di visualizzare
un messaggio con la descrizione dell'errore: la clausola "else"
dell'istruzione Select Case risulta particolarmente utile:
Sub
Prova()
On
Error GoTo
error_handler
error_handler:
Select
Case
Err.Number
Case 9
Case 13
Case Else 'qualunque
errore non gestito nei casi precedenti
MsgBox Err.Description, vbOkOnly, "Errore"
& Str$(Err.Number)
End Select
End
Sub
|
Un
altro sistema per gestire gli errori di run-time è
quello di utilizzare l'oggetto Debug, corrispondente alla
finestra Immediata già utilizzata in varie occasioni
durante le lezioni precedenti.
L'oggetto Debug dispone di due metodi: il primo è il
metodo Print, che visualizza nella finestra Immediata il valore
del parametro passato alla funzione; il secondo, che forse
non tutti conoscono, è il metodo Assert, che
sospende l'esecuzione del codice se si verifica una determinata
condizione.
Col metodo Assert il programmatore fa un'asserzione che, nel
caso in cui risulti non verificata, determina l'interruzione
del programma in modo che si possa capire il motivo per cui
la condizione espressa è risultata falsa.
Ad es., supponiamo che alla centesima iterazione di un ciclo
si verifichi un errore di cui non capiamo l'origine; potremmo
impostare un punto di interruzione all'inizio del ciclo, e
procedere passo-passo fino alla centesima iterazione: soluzione
alquanto noiosa.
Potremmo anche scrivere una routine di gestione degli errori
e impostare un breakpoint su questa routine: ma così
il codice sarebbe interrotto dopo che si è verificato
l'errore, mentre potrebbe essere utile intercettare l'errore
prima che si verifichi, sfruttando il fatto che sappiamo che
l'errore si verifica alla centesima iterazione.
Una soluzione conveniente è allora quella di usare
l'istruzione Debug.Assert:
For
i = 0 To 1000
Debug.Assert i<99
'istruzioni da eseguire ciclicamente
Next i |
Il
metodo Assert dell'oggetto Debug controlla se il contatore
del ciclo è minore di 99: in caso affermativo, l'esecuzione
del codice prosegue normalmente, altrimenti l'IDE di Visual
Basic interromperà l'esecuzione evidenziando in giallo
la riga "Debug.Assert i<99"; infatti, la prima
volta che la condizione espressa risulta falsa è quando
i=99, ovvero quando siamo alla centesima iterazione del ciclo,
e sappiamo che in questa iterazione si verifica il misterioso
errore. Procedendo passo passo con F8 potremo finalmente
capire cosa origina l'errore.
L'istruzione Debug.Assert è equivalente all'istruzione
Stop condizionata a un'istruzione If:
For
i = 0 to 1000
If i>=99 Then
Stop
End If
'istruzioni da eseguire ciclicamente
Next i |
L'oggetto
Debug è utilizzabile esclusivamente dall'ambiente di
progettazione, poiché una volta che il programma è
compilato le istruzioni Print e Assert vengono automaticamente
eliminate; infatti, quando avviamo un eseguibile già
compilato (un file *.exe per intenderci), non abbiamo a disposizione
una finestra Immediata in cui vedere il contenuto di variabili
o espressioni, né abbiamo modo di interrompere il codice
a nostro piacimento.
L'oggetto Debug è utilizzabile solo nella fase di debug
di un'applicazione, pertanto solo all'interno dell'ambiente
di progettazione, quando il codice può essere interpretato
istruzione per istruzione anziché essere compilato tutto
in una volta come quando si crea l'eseguibile.
La differenza sostanziale è che quando il codice è
interpretato, esso è eseguito per il tramite dell'ambiente
di progettazione, che si occupa di tradurre ogni istruzione
in codice macchina eseguibile dal processore; invece quando
il codice è compilato in un file eseguibile viene tradotto
subito in codice macchina: perciò la velocità
di esecuzione del codice compilato è maggiore di quella
del codice interpretato, poiché si elimina il passaggio
intermedio dell'interprete. In realtà per il Visual Basic
(almeno fino alla versione 6) il discorso è un po' più
complesso, perché è possibile scegliere se compilare
un progetto in "codice nativo" o in "p-code"
(pseudo-codice): il codice nativo è quello eseguito direttamente
dal processore, mentre il p-code è un codice intermedio
che necessita di un'ulteriore elaborazione da parte delle librerie
di run-time di Visual Basic, prima fra tutte la famigerata msvbvm60.dll
(per la versione 6).
Questa dll contiene routine di utilità generale, ad es.
per l'avvio e la chiusura dell'applicazione, oppure per la conversione
dei tipi di dati; anche l'interfaccia dell'oggetto Err, visto
nella lezione precedente, è fornita da questa libreria.
Essa si occupa, tra l'altro, anche di convertire lo pseudo-codice
in codice nativo: in effetti lo pseudo-codice è un codice
interpretato; tuttavia anche le applicazioni compilate direttamente
in codice nativo hanno bisogno dei servizi offerti da questa
libreria: ecco il motivo per cui quando si vuole distribuire
un'applicazione creata con Visual Basic è necessario
distribuire anche le librerie di run-time, che non sempre risultano
già installate nei pc destinatari dell'applicazione.
Passiamo
ora alla descrizione di alcune funzioni per la manipolazione
dei tipi di dati, cominciando dalle stringhe: alcune funzioni
le abbiamo già incontrate strada facendo, ma ora è
il caso di descriverle con qualche dettaglio in più.
Innanzitutto è bene chiarire una differenza che spesso
non viene percepita da alcuni programmatori: per la manipolazione
delle stringhe esistono funzioni che terminano con il simbolo
del dollaro $ e funzioni con lo stesso nome ma senza questo
simbolo; ad es. esiste la funzione Left() e la funzione
Left$(). La differenza dovrebbe essere chiara per coloro
che hanno avuto la possibilità di programmare con le
vecchie versioni del basic: il simbolo del dollaro infatti
era utilizzato (e può essere utilizzato ancora adesso,
per compatibilità) come carattere di dichiarazione
del tipo String; in altre parole, scrivere:
è
equivalente a scrivere:
Pertanto, le funzioni Left() e Left$() svolgono esattamente
lo stesso compito, differenziandosi unicamente per il tipo di
dati restituito e per il tipo di argomenti: Variant la prima,
String la seconda.
Ciò implica che la funzione Left$() sia più efficiente
della corrispondente Left() perché evita l'implicita
conversione delle variabili coinvolte da Variant a String: questo
è il motivo per cui solitamente si consiglia di usare
la versione "specializzata" della funzione al posto
della versione più generica.
La funzione Left$(), dunque, restituisce i primi n caratteri
di una determinata stringa, che viene passata alla funzione
come argomento insieme al numero n di caratteri:
restituisce
"pip". Se il numero di caratteri è 0, la funzione
restituisce una stringa nulla; se invece è maggiore della
lunghezza della stringa originale, la funzione restituisce tutta
la stringa: Left$("pippo", 6) restituisce "pippo".
Analogamente a Left$() c'è la funzione Right$() che restituisce
invece gli ultimi n caratteri (o, se si preferisce, i primi
n caratteri da destra):
Right$("pippo",
0) restituisce ""
Right$("pippo", 3) restituisce "ppo"
Right$("pippo", 6) restituisce "pippo"
|
E se uno vuole estrarre una parte intermedia di una stringa?
Usa la funzione Mid$(), che a differenza di Left$() e
Right$() necessita di un ulteriore parametro che rappresenta
la posizione iniziale della stringa da estrarre:
Mid$(sStringa
As String, lStart As
Long, [Lunghezza]) |
Ad
es.:
Mid$("pippo
va a sciare", 7, 4)
|
restituisce
"va a"
Il
secondo parametro indica la posizione del primo carattere della
sottostringa da estrarre, mentre il terzo specifica la lunghezza
della sottostringa; è chiaro che il secondo parametro
deve essere maggiore di zero, ma non è necessario che
sia minore della lunghezza della stringa originale: nel caso
in cui il parametro lStart sia maggiore della lunghezza di sStringa,
la funzione restituirà la stringa nulla.
La lunghezza della sottostringa da estrarre non è un
parametro obbligatorio, poiché ove mancasse la funzione
Mid$() restituirebbe tutti i caratteri a partire da lStart;
è un po' come usare la funzione Right$(), con
la differenza che non occorre sapere quanti caratteri estrarre.
Tuttavia le seguenti espressioni sono equivalenti:
Right$("pippo
va a sciare", Len("pippo va a sciare")
- 11)
Mid$("pippo va a sciare", 12) |
Se
sappiamo che la sottostringa che ci interessa (in questo caso
"sciare") comincia al dodicesimo carattere, possiamo
usare la funzione Right$() specificando come secondo parametro
la lunghezza della stringa originale meno la posizione iniziale
meno uno, poiché prima di sciare ci sono 12 - 1 = 11
caratteri da ignorare.
Spesso non si conosce a priori la posizione iniziale della sottostringa
da estrarre, né quella finale (ovvero la sua lunghezza):
in molti casi si possono ottenere queste informazioni con la
funzione InStr(), che ricerca una stringa all'interno
di un'altra; la sintassi completa è:
Instr(lStart,
sStringaOriginale, sStringaCercata, Confronto) |
Il
primo parametro determina la posizione da cui iniziare la
ricerca, il secondo indica la stringa originale in cui cercare
una particolare sequenza di caratteri specificata dal terzo
parametro; l'ultimo parametro determina il tipo di ricerca:
come già accennato nella lezione 20, è possibile
specificare la costante vbTextCompare per una ricerca case
insensitive, che cioè ignora la differenza tra minuscole
e maiuscole; oppure la costante vbBinaryCompare per una ricerca
case sensitive, che tiene conto della differenza tra maiuscole
e minuscole perché considera il valore binario di ogni
carattere delle stringhe confrontate, valore determinato dal
corrispondente codice ANSI.
Esiste poi anche la costante vbDatabaseCompare, ma è
usata solo con Microsoft Access.
Se questo parametro viene omesso, l'impostazione di default
è quella di usare un confronto binario (quindi case
sensitive), a meno che sia specificata l'istruzione Option
Compare Text nella sezione delle dichiarazioni del form
o del modulo in cui è utilizzata la funzione InStr().
L'istruzione Option Compare è analoga all'istruzione
Option Explicit, ma determina a livello di modulo l'impostazione
predefinita per confrontare le stringhe: come la funzione
InStr(), anche Option Compare accetta alternativamente tre
tipi di confronto:
Option
Compare Text 'confronto testuale
Option Compare Binary 'confronto binario
Option Compare Database |
La
funzione InStr() restituisce quindi la posizione iniziale della
stringa cercata all'interno di quella originale: si tratta ovviamente
di un numero intero positivo; infatti se la ricerca fallisce,
la funzione restituisce 0, interpretabile come valore logico
False nelle espressioni condizionali.
Un tipico esempio in cui si fa ricorso alla funzione InStr()
è quello di separare il nome di un file dal suo percorso,
come già visto nella lezione 20: talvolta però
risulta più comodo usare la funzione InStrRev(),
del tutto analoga a InStr() ma che ricerca la sottostringa a
partire dalla fine della stringa iniziale anziché dall'inizio.
Così, se voglio estrarre il nome di un file dal suo percorso,
basterà scrivere
sNomeFile
= Mid$(sPath, InStrRev(sPath, "\")+1) |
La
funzione InStrRev() restituisce la posizione del primo backslash
a partire dal fondo di sPath (quindi in effetti la posizione
dell'ultimo backslash); ottenuta questa informazione, la funzione
Mid$() senza il terzo argomento restituisce tutto ciò
che segue l'ultimo backslash, ovvero il nome del file: notate
che se non avessimo aggiunto 1 al valore restituito da InStrRev(),
la funzione Mid$() avrebbe restituito anche l'ultimo backslash
seguito dal nome del file. |