Eseguire
una ricerca veloce nell' archivio delle risorse di Visual Basic
Italia®:
|
Preso
dall'archivio...
Premere il pulsante sotto per accedere direttamente ad un articolo
o ad un esempio preso in modo casuale dall'archivio.
|
Ultimo
e-book pubblicato:
"INTRODUZIONE
AI CSS"
Lo scopo del CSS language è quello di controllare lo
stile dei vari elementi che concorrono a formare un
documento HTML.
Si può operare in tre modi collegamento ad un foglio di stile
esterno;definizione degli stili all'inizio
del documento utilizzo della proprietà style all'interno
di un Tag HTML (embedded style). Questo e-book introduttivo
servirà per apprendere tali nozioni fondametali dei fogli
di stile.
Prezzo: € 0.0.
|
Contatti.
Utilizzare l'email generica per domande relative al sito:
oppure scrivere
ad un responsabile di area.
Responsabile
del sito: >Andrea
Martelli
Responsabile area "Corso di VB":
> Giorgio Abraini
|
|
|
|
Corso
di Visual Basic: lezione 27 |
L'ultima
lezione si era conclusa con alcuni dettagli da definire a
proposito della ricerca di testo: ad es. occorre tener conto
della direzione di ricerca scelta dall'utente.
Per fare ciò dobbiamo introdurre un'altra variabile
che ci indichi la direzione: risulta comodo che questa variabile
sia di tipo numerico e che abbia valore +1/-1 a seconda che
la direzione di ricerca sia verso il basso o verso l'alto.
Questo perché per cercare un testo verso il basso ci
è venuto naturale incrementare un contatore che indichi
la posizione corrente all'interno del testo; analogamente,
per cercare un testo verso l'alto sarà sufficiente
decrementare quel contatore, senza modificare pesantemente
l'algoritmo di ricerca.
Il flag di direzione può essere impostato in
questo modo:
lDirezione
= IIf(optSu.Value = True,
-1, 1) |
Se
la direzione scelta dall'utente è verso l'alto, il
flag assume valore -1, in modo che l'incremento del contatore
può essere modificato semplicemente passando da un
loop del tipo:
'cerca
la prima cifra
Do
lInizio = lInizio + 1
Loop Until (Mid$(.Text, lInizio,
1) Like "#") Or
(lInizio > Len(.Text))
|
A
uno del tipo:
'cerca
la prima cifra
Do
lInizio = lInizio + 1 * lDirezione
Loop Until (Mid$(.Text, lInizio,
1) Like "#") Or
(lInizio > Len(.Text)) Or
(lInizio < 1)
|
Come
si diceva, se la direzione scelta è verso l'alto il flag
lDirezione sarà -1 e quindi il contatore (in questo caso
lInizio) sarà decrementato anziché incrementato.
Se la direzione scelta è "tutto", siamo in
una situazione analoga alla direzione "giù",
solo che bisognerà cercare dall'inizio anche se il cursore
si trova a metà del testo:
If
optTutto.Value Then
lInizio = 0
Else
lInizio = .SelStart + .SelLength
End If
|
Quando
invece il testo è cercato non incrementando un contatore
ma usando la funzione InStr, nel caso di ricerca verso l'alto
basterà utilizzare la funzione InStrRev:
'cerca
il testo puro iniziale
If lDirezione = 1 Then
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0), lTipoRicerca)
Else
lInizio = InStrRev(.Text, sTestoPuro(0), lInizio, lTipoRicerca)
End If
|
Dobbiamo
però ricordare che, se il testo va cercato verso l'alto,
non va bene impostare il punto di partenza aggiungendo SelLength
a SelStart, perché dovendo cercare "all'indietro"
continueremmo a trovare sempre lo stesso testo: infatti il punto
di partenza della ricerca sarebbe successivo al testo da cercare.
Quando il testo andava cercato verso il basso, dovevamo impostare
il punto di partenza a una posizione successiva a SelStart;
se il testo va cercato verso l'alto, il punto di partenza deve
precedere SelStart.
Pertanto possiamo scrivere:
If
optSu.Value Then
lDirezione = -1
lInizio = .SelStart
Else
lDirezione = 1
If optGiu.Value Then
lInizio = .SelStart + .SelLength
Else 'optTutto
lInizio = 0
End If
End If
|
In
questa If abbiamo distinto i tre casi (direzione su, giù,
tutto), e nel caso in cui la direzione scelta sia "tutto"
abbiamo impostato a zero la variabile lInizio; questo perché,
come si è detto prima, la ricerca deve coinvolgere tutto
il testo del file, indipendentemente da dove si trova il cursore.
Tuttavia, ciò è vero solo quando si procede alla
prima ricerca del testo: quando si cerca l'occorrenza successiva
sarebbe sbagliato impostare di nuovo a zero il punto di partenza,
altrimenti la routine continuerebbe a trovare sempre lo stesso
testo (la prima occorrenza dall'inizio del file).
Si potrebbe usare un flag per indicare se la ricerca su "tutto"
il file è all'inizio o sta continuando, tuttavia in questo
caso potrebbe risultare comodo sfruttare l'evento Click dell'OptionButton
in questo modo:
Private
Sub
optTutto_Click()
frmNotePad.txtFile.SelStart = 0
End Sub |
Quando
l'utente seleziona la direzione di ricerca "tutto",
il cursore viene automaticamente impostato all'inizio del file:
pertanto non c'è più bisogno dell'istruzione lInizio=0
nella routine di ricerca:
If
optSu.Value Then
lDirezione = -1
lInizio = .SelStart
Else
lDirezione = 1
lInizio = .SelStart + .SelLength
End If |
Così,
con qualche piccola modifica, abbiamo implementato la ricerca
in tutte le direzioni. Resta da trattare il caso della ricerca
dei caratteri jolly.
Per permettere all'utente di cercare uno dei caratteri utilizzati
come jolly (*, ?, #) occorre far capire alla routine di ricerca
che il carattere inserito va preso come tale e non come simbolo
che rappresenta un set di caratteri.
La tecnica tradizionale per ottenere questo scopo è quella
di abilitare una "sequenza di escape": le sequenze
escape sono sequenze di caratteri che attribuiscono ai caratteri
successivi un significato differente da quello normale.
Ad es., un problema comune che si incontra in Visual Basic è
quello di assegnare a una variabile stringa una stringa comprensiva
di virgolette; se io scrivo:
la
variabile sStringa conterrà le lettere "c",
"i", "a", "o", ma non le virgolette
che racchiudono queste lettere: questo perché le virgolette
in Visual Basic (come in altri linguaggi) identificano proprio
le stringhe; se non usassimo le virgolette, Visual Basic penserebbe
che "ciao" è il nome di una variabile o una
funzione o un'istruzione.
Ma se io volessi assegnare a sStringa la parola "ciao"
CON le virgolette, come potrei fare?
Un modo è quello di concatenare le stringhe utilizzando
il codice ascii delle virgolette:
sStringa=chr$(34)
& "ciao" & chr$(34) |
Ma
un altro modo è quello di usare le virgolette in un modo
particolare, ovvero:
Ogni
sequenza di tre virgolette rappresenta
le virgolette ripetute
una sola volta.
Ripeterle solo due volte non basterebbe, perché sarebbero
confuse con la stringa nulla "", perciò occorre
ripeterle tre volte: è come se le prime virgolette rappresentassero
l'inizio della stringa, e le seconde virgolette fossero un modo
per dire: "guarda che il carattere successivo (cioè
le terze virgolette) vanno intese come testo, e non come carattere
di chiusura della stringa"; lo stesso vale per le tre virgolette
finali. In altre parole, in questo contesto le virgolette vengono
usate come sequenza di escape, che alterano il normale significato
attribuito alle virgolette.
Un problema analogo si incontra nel linguaggio C quando occorre
specificare il carattere "\" come testo: infatti questo
carattere è solitamente utilizzato come carattere di
escape per rappresentare particolari sequenze di caratteri.
Tornando a noi, dobbiamo inventarci un carattere di escape che
inibisca l'uso dei caratteri jolly; supponiamo di usare proprio
il backslash "\" come carattere di escape: il testo
"pippo*" significa come al solito "pippo"
seguito da qualunque cosa, ma "pippo\*" significa
semplicemente "pippo*", dove l'asterisco questa volta
non è un carattere jolly ma è un carattere come
gli altri.
Tenete presente che si potrebbe utilizzare il carattere di escape
anche per altri scopi, ad es. per indicare il ritorno a capo:
potremmo decidere cioè che la sequenza "\a"
indichi il ritorno a capo, quindi: "pippo\apiero"
non sarebbe altro che:
Questa
è una soluzione che risulta comoda data la difficoltà
di rappresentare certi caratteri su una sola riga o perché
il tasto corrispondente ("invio" nel caso del ritorno
a capo) è associato ad altre funzioni, ad es. alla pressione
di un pulsante.
Ad ogni modo, per ora limitiamoci ai caratteri jolly.
Ripercorriamo l'analisi della stringa immessa dall'utente nel
campo "trova": dapprima si cercano (per eliminarli)
gli asterischi iniziali e finali; prima di eliminare gli asterischi
finali, però occorre controllare che non siano preceduti
dal carattere escape "\":
lFine
= Len(txtTrova.Text)
Do While Mid$(txtTrova.Text,
lFine, 1) = "*"
lFine = lFine - 1
Loop
If Mid$(txtTrova.Text, lFine, 1) = "\"
Then 'carattere
escape
lFine = lFine + 1
End If
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio
+ 1) |
Il
puntatore lFine viene spostato in avanti per non perdere l'ultimo
asterisco.
L'analisi prosegue poi cercando i testi puri e verificando se
sono preceduti o seguiti da una cifra o da un carattere: nel
caso in cui siano presenti caratteri escape, la routine confonderebbe
il carattere "\" con un carattere qualunque, "distorcendo"
la ricerca dei "veri" testi puri; ad es., se la stringa
cercata dall'utente fosse: "*\#pippo*\?piero" i testi
puri trovati sarebbero "\" e "piero" anziché
"#pippo" e "?piero".
In teoria dovremmo "aggiustare" la stringa da cercare
in modo che la routine estragga correttamente i testi puri,
ma in realtà sembra più conveniente lasciare l'aggiustamento
a quando i testi puri sono già stati estratti: tutto
quello che occorre fare è sostituire i caratteri escape
con il carattere successivo:
If
Right(sTestoPuro(0), 1) = "\" Then
Mid$(sTestoPuro(0), Len(sTestoPuro(0)), 1) = Mid$(txtTrova.Text,
_
InStr(1, txtTrova.Text, sTestoPuro(0)) + Len(sTestoPuro(0)),
1)
End If
If Right(sTestoPuro(1), 1) = "\" Then
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text,
_
InStr(1, txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)),
1)
End If |
Inoltre,
bisogna reimpostare l'elemento 1 dei vettori lCifra e lCarattere,
perché se l'ultimo testo puro contiene un carattere escape
significa che un "?" o un "#" sono stati
erroneamente interpretati come caratteri jolly:
If
Right(sTestoPuro(1), 1) = "\" Then
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text,
_
InStr(1, txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)),
1)
If Right$(sTestoPuro(1),
1) = "#" Then
lCifra(1) = lCifra(1) - 1
ElseIf Right$(sTestoPuro(1),
1) = "?" Then
lCarattere(1) = lCarattere(1) - 1
End If
End If |
Prima
di iniziare la ricerca occorre infine modificare opportunamente
il contenuto del campo trova, poiché è quello
che, tramite l'operatore Like, determina se il testo trovato
corrisponde effettivamente al testo da cercare:
txtTrova.Text
= Replace(txtTrova.Text, "\*", "[*]")
txtTrova.Text = Replace(txtTrova.Text, "\#",
"[#]")
txtTrova.Text = Replace(txtTrova.Text, "\?",
"[?]") |
L'uso
delle parentesi quadre permette all'operatore Like di considerare
i caratteri "jolly" come caratteri normali; le parentesi
quadre rappresentano quindi una sorta di sequenza escape per
l'operatore Like.
Occorre anche salvare la stringa di ricerca inserita dall'utente
per evitare che nel campo "trova" restino le modifiche
appena fatte: utilizziamo allo scopo una variabile sTestoCercato.
La nostra routine di ricerca è quindi diventata:
Dim
lTipoRicerca As Long
Dim lCifra(1) As
Long
Dim lCarattere(1) As
Long
Dim lInizio As
Long
Dim lFine As
Long
Dim lInizioTemp As
Long
Dim lFineTemp As
Long
Dim lDirezione As
Long
Dim sTestoPuro(1) As
String
Dim sTestoTrovato As
String
Dim sTestoCercato As
String
lInizio = 0: lFine = 0
Erase sTestoPuro
Erase lCifra
cmdTrova.Enabled
= False
cmdChiudi.Enabled = False
Me.MousePointer = vbHourglass
sTestoCercato
= txtTrova.Text
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text,
lInizio, 1) = "*"
lFine
= Len(txtTrova.Text)
Do While Mid$(txtTrova.Text,
lFine, 1) = "*"
lFine = lFine - 1
Loop
If Mid$(txtTrova.Text, lFine, 1) = "\"
Then 'carattere
escape
lFine = lFine + 1
End If
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine -
lInizio + 1)
'primo
testo "puro"
lInizio = 0
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text,
lInizio, 1) Like "[*?#]"
lFine = lInizio
Do
lFine = lFine + 1
Loop Until Mid$(txtTrova.Text,
lFine, 1) Like "[*?#]"
Or lFine > Len(txtTrova.Text)
'If Mid$(txtTrova.Text, lFine -
1, 1) = "\" Then
' Mid$(txtTrova.Text, lFine - 1, 1) = Mid$(txtTrova.Text,
lFine, 1)
'End If
sTestoPuro(0) = Mid$(txtTrova.Text, lInizio, lFine -
lInizio)
lCifra(0)
= InStr(1, Replace(Left$(txtTrova.Text, lInizio - 1),
"*", ""), "#") - 1
lCarattere(0) = InStr(1, Replace(txtTrova.Text, "*",
""), sTestoPuro(0)) - 1
'ultimo
testo "puro"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text,
lFine, 1) Like "[*?#]"
And lFine > 1
lFine = lFine - 1
Loop
lInizio = IIf(lFine > 1, lFine, 2)
Do
lInizio = lInizio - 1
Loop Until Mid$(txtTrova.Text,
lInizio, 1) Like "[*?#]"
Or lInizio <= 1
If Mid$(txtTrova.Text,
lInizio, 1) Like "[*?#]"
Then
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio + 1, lFine
- lInizio)
Else
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio, lFine -
lInizio + 1)
End If
lCifra(1)
= Len(Replace(Mid$(txtTrova.Text, lFine + 1), "*",
"")) - _
InStrRev(Replace(Mid$(txtTrova.Text, lFine + 1), "*",
""), "#")
If lCifra(1) = Len(Replace(Mid$(txtTrova.Text,
lFine + 1), "*", "")) Then
lCifra(1) = -1
End If
lCarattere(1)
= Len(Replace(txtTrova.Text, "*", ""))
- _
InStrRev(Replace(txtTrova.Text, "*", ""),
sTestoPuro(1)) - Len(sTestoPuro(1))
If
Right(sTestoPuro(0), 1) = "\" Then
Mid$(sTestoPuro(0), Len(sTestoPuro(0)), 1) = Mid$(txtTrova.Text,
_
InStr(1, txtTrova.Text, sTestoPuro(0)) + Len(sTestoPuro(0)),
1)
End If
If Right(sTestoPuro(1),
1) = "\" Then
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text,
_
InStrRev(txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)),
1)
If Right$(sTestoPuro(1),
1) = "#" Then
lCifra(1) = lCifra(1) - 1
ElseIf Right$(sTestoPuro(1),
1) = "?" Then
lCarattere(1) = lCarattere(1) - 1
End If
End If
If
InStr(1, txtTrova.Text, sTestoPuro(0), vbBinaryCompare)
= _
InStrRev(txtTrova.Text, sTestoPuro(1), , vbBinaryCompare)
Then
'il primo e l'ultimo testo puro
coincidono
sTestoPuro(1) = ""
End If
txtTrova.Text
= Replace(txtTrova.Text, "\*", "[*]")
txtTrova.Text = Replace(txtTrova.Text, "\#",
"[#]")
txtTrova.Text = Replace(txtTrova.Text, "\?",
"[?]")
With
frmNotePad.txtFile
If optSu.Value Then
lDirezione = -1
lInizio = .SelStart
Else
lDirezione = 1
lInizio = .SelStart + .SelLength
End If
lFine = 0
lTipoRicerca = IIf(chkCaseSens.Value = vbChecked, vbBinaryCompare,
vbTextCompare)
Do
If lCifra(0) >= 0 Then
'cerca
la prima cifra
Do
lInizio = lInizio + 1 * lDirezione
Loop Until (Mid$(.Text,
lInizio, 1) Like "#")
Or (lInizio > Len(.Text))
Or (lInizio < 1)
If lInizio > Len(.Text)
Then
lInizio = 0
Else
lFine = lInizio
End If
Else
If Len(sTestoPuro(0)) Then
'cerca il testo puro iniziale
If lDirezione = 1 Then
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0), lTipoRicerca)
Else
lInizio = InStrRev(.Text, sTestoPuro(0), lInizio, lTipoRicerca)
End If
End If
lFine = lInizio + Len(sTestoPuro(0)) - 1
End If
If lInizio Then
Do
If lCifra(1) >= 0 Then
'cerca l'ultima cifra
Do
lFine = lFine + 1
Loop Until (Mid$(.Text,
lFine, 1) Like "#")
Or (lFine > Len(.Text))
Or (lFine < 1)
If lFine > Len(.Text)
Then lFine = 0
Else
lFine = lFine + 1
'cerca il testo puro finale
If Len(sTestoPuro(1)) Then
lFine = InStr(lFine + 1, .Text, sTestoPuro(1), lTipoRicerca)
End If
End If
If lFine = 0 Then
Exit Do
Else
lInizioTemp = lInizio - IIf(lCifra(0) < 0, IIf(lCarattere(0)
< 0, 0, lCarattere(0)), lCifra(0))
If lCifra(1) >= 0 Then
lFineTemp = lFine + lCifra(1)
Else
lFineTemp = lFine + Len(sTestoPuro(1)) + IIf(lCarattere(1)
< 0, 0, lCarattere(1)) - 1
End If
End If
DoEvents
sTestoTrovato = Mid$(.Text, lInizioTemp, lFineTemp -
lInizioTemp + 1)
Loop Until IIf(lTipoRicerca
= vbBinaryCompare, (sTestoTrovato Like
txtTrova.Text), UCase$(sTestoTrovato) Like
UCase$(txtTrova.Text))
If lFine Then
.SelStart = lInizioTemp - 1
.SelLength = lFineTemp - lInizioTemp + 1
End If
Else
'cifra o testo iniziale non trovati
MsgBox "Testo non trovato", vbOKOnly + vbExclamation,
App.Title
End If
Loop While (lFine = 0) And
(lInizio < Len(.Text)) And
(lInizio > 0)
End With
cmdTrova.Enabled = True
cmdChiudi.Enabled = True
Me.MousePointer = vbDefault
txtTrova.Text
= sTestoCercato
If frmTrova.Visible Then
txtTrova.SetFocus
End If
End
Sub
|
Per
le prove che mi è stato possibile fare, sembra che la
routine funzioni a dovere.
Naturalmente è possibile tutto un lavoro di ottimizzazione
e di "ripulitura" del codice per renderlo più
snello ed efficiente, ma per ora mi fermo qua. |
|