Salta al contenuto principale

i 3 dizionari di NVDA, terza parte

adriano barbieri su uiciechi.it, 2008-06.

Terza parte.

Ed ora, un po' di pubblicità

:

*********

Adesso che abbiamo anche compreso cosa sono i gruppi tra parentesi tonde e come sfruttarne la loro potenzialità, per crearci il nostro "asterisco" personalizzato,
in questo articolo proverò ad elencare alcuni metodi per matchiare e manipolare la vocalizzazione di alcuni tipi di vocaboli.

Inoltre, proverò a descrivere alcune tecniche utili che consentono di invertire, filtrare, e non far vocalizzare parti di una parola e anche, quando possibile,
costruire un'espressione regolare che da sola è in grado di matchare anche alcune eventuali varianti di un vocabolo.

La peculiarità delle Regex, al pari di tutti i linguaggi di programmazione,come già detto nella seconda parte di questa serie di articoli, è consentire
di memorizzare parti di una stringa di matching, dandoci poi la possibilità di richiamarne il valore memorizzato semplicemente riferendoci al numero logico
assegnato al gruppo.

Vi ricordo che il numero logico assegnato a un gruppo è un valore rappresentato dai numeri "1, 2, 3", eccetera, abbinato ad ogni gruppo rappresentato dalla
coppia di parentesi tonde"()" aperta e chiusa, , da sinistra verso destra: Tenete presente che occorre contare anche i gruppi che potrebbero essere stati
nidificati all'interno di un altro gruppo, perché anch'essi hanno un loro numero logico abbinato.

E’ possibile quindi richiamare e far vocalizzare il contenuto del matching dei gruppi presenti nella nostra espressione regolare, ma non c'è una regola
che imponga il richiamo in un ordine definito, ciò vuol dire che possiamo farlo nell'ordine a nostra scelta, ad esempio:

• Sempre dal dizionario temporaneo, facciamo invio su aggiungi.
• Nel primo campo digitiamo: "(N)(V)(D)(A)", come sempre senza le virgolette. Fate attenzione alle parentesi tonde.
• I 4 gruppi della Regex memorizzeranno ognuno la lettera assegnatagli.

Provate ora a richiamare i gruppi inserendo nel campo in sostituzione il numero logico precedendolo dalla barra diagonale rovesciata, così: "\1 \2 \3 \4",
notare che lo spazio tra un richiamo e l'altro non è obbligatorio, è stato inserito solo per generare uno "spelling", altrimenti la parola verrebbe letta
tutta di un fiato.

Questo anche a dimostrare che è possibile l'inserimento di caratteri o intere parole, nonché punteggiature e/o spaziatura fra i vari richiami.

Se avete una versione di NVDA recente, in cui il modulo dizionario è munito di due caselle di controllo in aggiunta, mi raccomando di marcare la casella
di controllo della espressione regolare.

Così facendo se scriverete: "NVDA" da blocco note e farete vocalizzare la riga digitata, non si avvertirà nulla di strano, ma provate a mescolare l'ordine
dei numeri logici presenti nel campo sostituzione e provate a far vocalizzare la riga precedentemente editata e vedrete che avrete una lettura molto diversa.

Perché funzioni, ovviamente serve che il matching sia true, cioè vero, quindi è neccessario che il testo editato nel blocco note corrisponda appieno a
ciò
che è stato previsto nel campo di ricerca, sia per quanto riguarda l'ordine, sia per quanto riguarda tipo e numero dei caratteri. Ulteriori combinazioni
di caratteri digitati manterranno il loro stato originale e non verranno invertite, e verranno lette così come sono state digitate.

Nulla ci impedisce a questo punto, di sfruttare i gruppi alla stregua di filtri. Potremmo far sì che venga vocalizzata solo una parte del matching, scartando
ciò che non serve, provate ad esempio a togliere il richiamo di uno o due gruppi dal campo sostituzione, oppure ripetete alcuni richiami, la lettera o
la parola memorizzata nei gruppi corrispondenti verrà ripetuta per ogni richiamo abbinato al gruppo.

Fate poi vocalizzare la riga precedentemente digitata nel blocco note e noterete le differenze.

Con un poco di fantasia, è possibile immaginare che il sistema ci permetta di invertire e/o filtrare e/o far ripetere intere parole e/o parti di esse.

Vediamo adesso alcuni metodi per matchare una parola ambigua, per il semplice fatto che la stessa parola, a seconda del contesto cambia la posizione dell'accento,
nella lingua italiana ce nè un'infinità, eccone un piccolo esempio:

• "Desidèri, desìderi".
• "Rètina, retìna".
• "Pòrtali, pòrtali".
• "Tèndine, tendìne.

Un particolare che ci consente di identificare il contesto, anche se a volte incorrono eccezioni, è l'articolo oppure è l'aggettivo che precede la parola,
in altri casi la congiunzione che la segue, oppure la parola adiacente.

Innanzitutto occorre capire la nostra sintesi vocale cosa fa già da sola, perché molte sintesi vocali hanno nel proprio "motore" degli algoritmi dedicati
a interpretare nel modo migliore quelle parole ambigue. Però, siccome molto spesso non ci soddisfano i risultati, perché non è possibile prevedere tutto,
accade che qualche cosa viene tralasciata per non perdere tempo oppure si opta per una via di mezzo, quindi, dobbiamo per forza intervenire.

Ecco qualche esempio:

• "Mi fa male il tendine".
• "Le tendine erano azzurrine".

Prendiamo ad esempio questa ultima parola, riferita al tendine di Achille... quello che arriva fino al tallone.... "Tendine", può essere anche al plurale:
"Tendini" e quindi bisognerà tenerlo presente.

la nostra Regex dovrà riconoscere dall'articolo se si tratta del tèndine o di tendìne. Quindi, potremmo creare un gruppo di controllo da anteporre al gruppo
con la parola da matchare in oggetto, contenente una lista degli articoli determinativi ammessi, basterà matchare l'ultima lettera più uno spazio che dovrebbe
essere parte dell possibile articolo che precede la parola "Tendin", un altro gruppo di controllo invece, si occuperà di matchare l'ultima vocale della
parola, per determinare se essa sarà singolare o plurale, una cosa come questa ad esempio:

# Tendine/i.

"([iIlL]\W)(TENDIN|[tT]endin)([eEiI]\W)       \1téndin\3      1       1".

Un sistema un po' blando, ma che permette di vocalizzare correttamente:

• "al tendine".
• "col tendine".
• "dal tendine".
• "del tendine".
• "nel tendine".
• "sul tendine".

Ma anche:

• "ai tendini"
• "dai tendini"
• "dei tendini"
• "coi tendini"
• "nei tendini"
• "sui tendini"

Insomma qualsiasi parola, che termini con uno dei caratteri racchiusi nella classe fra parentesi quadre contenuta nel primo gruppo dell'espressione regolare.
Da notare anche che questi caratteri devono avere un carattere separatore prima del secondo gruppo, ed è indicato dal carattere speciale: "\W" (barra diagonale
rovesciata doppia vu maiuscola), ma anche un "\s" (barra diagonale rovesciata s minuscola) andrebbe bene, questa indica uno spazio.

La Regex però accetterà anche una frase del genere: "Questi tendini mi fanno male".

Ma non questa: "Questo tendine mi fa male".

Sarà necessario aggiungere nella classe del primo gruppo la vocale finale, minuscola e maiuscola "oO", dell'aggettivo dimostrativo perché venga matchata
correttamente.

Questo sistema è funzionale nella sua semplicità, ma lascia passare parole, qualsiasi parola che termini con una delle vocali ammesse nella classe, anche
se insensate come:

• "fdghfi tendini".
• "dsfhfl tendine".

Per affinare la nostra Regex occorrerebbe aggiungere un ulteriore gruppo a sinistra, che consenta il matching solo se la frase è a inizio riga o preceduta
da un carattere separatore come uno spazio, una tabulazione, una punteggiatura, eccetera.

Io normalmente uso questo gruppo: "(\W|^)".

Se questo gruppo lo mettiamo all'inizio di una Regex, esso, nella maggior parte dei casi, impedisce che vi siano caratteri appiccicati sulla immediata
sinistra
della frase da matchare. Se il gruppo poi lo si fa seguire da un'altro che contenga i nostri articoli o /e aggettivi, non saranno ammesse confusioni di
sorta.

La Regex allora potrebbe esser così:

"(\W|^)(([iI]|[aAiIuU][iIlLnN]|[dDcCnNsS][aAeEoOuU][iIlL])\s)(TENDIN|[tT]endin)([eEiI]\W)     \1\2téndin\5    1       1".

Se fate caso alle classi contenute nel secondo gruppo, ci sono gli articoli determinativi ammessi, sia in minuscolo, sia in maiuscolo: "i, al, il, un,
dal,
del, col, nel, nei, sul".

L'ultimo gruppo, in entrambi gli esempi, si occupa di far terminare la nostra parola "Tendin" con la sola vocale che determinerà se sarà singolare o plurale.

Ovviamente se la nostra parola dovesse essere priva di articolo alla sua sinistra, il matching della Regex sarà false e non verrà eseguita.

Notare che, per limitare la lunghezza della riga, non ho inserito il controllo sugli aggettivi dimostrativi "questi e questo", ma una seconda Regex come
questa risolverebbe il problema:

# Tendine/i /questi/o.

"(\W|^)([qQ][uU][eE][sS][tT][iIoO]\W)(TENDIN|[tT]endin)([eEiI]\W)     \1\2tendin\4    1       1".

Le classi fra parentesi quadre permettono il matching di un solo carattere contenuto al loro interno, ma se una classe è affiancata da un altra classe,
il carattere si somma al successivo formando la parola come se fosse così: "[qQ]+[uU]+[eE]+[sS]+[tT]+[iIoO]".

Il matching fra multiple combinazioni di classi multiple ha termine appena viene raggiunto il carattere "|" (linea verticale che significa or), il quale
determina una alternativa e di conseguenza un'altra serie di combinazioni di classi ammesse nel matching.

Infatti, alcuni articoli determinativi sono di un carattere, altri composti da due, e altri da tre caratteri, il tutto è poi raggruppato fra parentesi
tonde
nidificate solo perché tutte le classi devono avere uno spazio separatore: (articolo+spazio+parola).

Raggruppandole fra parentesi ho specificato che tutte sono soggette all'aggiunta di un separatore come "\W", o "\s", così è stato sufficiente indicare
il
separatore una sola volta per tutte quante, altrimenti sarebbe stato neccessario, alla fine di ogni combinazione di classi, quindi prima di ogni "|" (barra
verticale or), inserire il carattere speciale "\s" (barra diagonale rovesciata s minuscola).

Vediamo ora un altro metodo di matching che sfrutta il carattere speciale "." (punto fermo), indicante che è accettabile un carattere ignoto. E se volessimo
un numero definito di caratteri ignoti? Ecco un esempio anche se parziale:

La parola "Fracassare", quante varianti ha?

• "Fracassarmela".
• "Fracassarmele".
• "Fracassarmeli".
• "Fracassarmelo"...

Quì ce la caveremo con questa Regex:

# Fracassarmela/e/i/o.

"(FRACASSARMEL|[fF]racassarmel)([aAeEiIoO]\W) Fracassàrmel\2  1       1".

Funzionerebbe anche così:

# Fracassarmela/e/i/o.

"(FRACASSARMEL|[fF]racassarmel)(.\W)  Fracassàrmel\2  1       1".

Abbiamo sostituito l'intera classe dell'ultimo gruppo con un singolo "." (punto fermo). In pratica, nel caso sopracitato ci serve un solo carattere per
l'identificazione della parola, la vocale finale della parola stessa, e si sa che sono solo quattro in tutto, anche se il solo punto fermo lascerà passare
altri caratteri oltre alle sole vocali richieste, ma può andar bene ugualmente, dipende da quanto si vuol essere pignoli.

Ma se fosse invece:

• "Fracassartela"
• "Fracassartele"
• "Fracassarteli"
• "Fracassartelo"

Be'! un'altra regex in supporto come questa andrebbe bene:

"(FRACASSARTEL|[fF]racassartel)(.\W)  Fracassàrtel\2  1       1".

E siamo a 2 Regex!ma se fosse:

• "Fracassargliela"
• "Fracassarmelo"
• "fracassarsele"
• "fracassarglieli"

In questo particolare caso, considerando che la parola "Fracassar" e ricorsiva in quasi tutte le varianti, possiamo risolvere la questione con una sola
Regex che preveda un numero variabile di caratteri ignoti, e ci vengono in soccorso le parentesi graffe.

Esse, come detto nel precedente articolo, ci permetttono di definire un limite minimo e massimo di rippetizioni dell'ultimo carattere che le precede, anche
se si tratta di un carattere speciale, il punto fermo, come nel nostro caso, ed ecco cosa otteniamo:

# Fracassargliela/mela/sela/tela/e/i/o.

"(FRACASSAR|[fF]racassar)(.{4,6}\W)   Fracassàr\2     1       1".

La riga di commento dopo il carattere "#" elenca tutto ciò che la regex dovrebbe accettare in grandi linee, quindi, se il matching della parola: "fracassar",
sarà true, saranno ammessi in aggiunta un minimo di quattro caratteri fino ad un massimo di sei caratteri ignoti, appiccicati alla parola "fracassar".

Purtroppo però, anche "fracassardxdghf" sarà accettata, ma contiamo che nessuno userà mai una parola così palesemente errata, sicuri di non sbagliare.

Ecco un'altro esempio:

# Precluderla/e/i/o/gli.

"(PRECLUDER|[pP]recluder)(.{2,3}\W)   Preclùder\2     1       1".

Vediamo un altro metodo che sfrutta ancora le classi, ossia un gruppo di caratteri o numeri racchiusi fra parentesi quadre.

Ne abbiamo già parlato, e abbiamo visto che per ogni classe è preso in considerazione solo un carattere alla volta tra quelli presenti nella lista per
ogni
classe, e sottolineo per ogni classe.

Questo significa che, affiancare delle classi tra di loro, ci consente di formare delle parole intere, esattamente come se ad esempio scrivessimo: "nvda",
o "[n][v][d][a]", con il vantaggio però, che le classi ci consentono di inserire delle varianti, tramite l'aggiunta di altri caratteri all'interno di ogniuna,creando
delle liste ad esempio così:

"[nN][vV][dD][aA]".

In questo modo abbiamo detto che la parola "nvda" è ammessa sia in minuscolo e anche in maiuscolo.

Comodo per matchare parole un po' più lunghe come ad esempio questa:

# Grondano/ino.

"(GROND|[gG]rond)([aAiI][nN][oO]\W)   Grónd\2 1       1".

Come potete vedere, la Regex appena descritta consente di matchare:

• "Grondano"
• "Grondino"

Oppure quest'altra un po' più complessa, ma sempre sfruttante lo stesso principio delle classi affiancate:

# Grassocce/i/ia/io.

"(GRASSOCC|[gG]rassocc)([eEiI]\W|([iI][aAoO])\W)      Grassòcc\2      1       1".

Questa Regex consente di matchare:

• "Grassocce"
• "Grassocci"
• "Grassoccia"
• "Grassoccio"

Un ultimo esempio:

# Tingerci/mi/ne/si/ti/vi/gli.

"(TINGER|[tT]inger)([cCmMnNsStTvV][eEiI]|[gG][lL][iI]\W)      Tìnger\2        1       1".

Questa Regex consente di matchare:

• "Tingerci"
• "Tingermi"
• "Tingerne"
• "Tingersi"
• "Tingerti"
• "Tingervi"
• "Tingergli"

Dalla stesura del primo articolo dedicato ai dizionari di Nvda è passato un po' di tempo, in quel periodo non ero ancora molto pratico a gestire le punteggiature
per matchare le famose emoticons, le faccine che spesso vengono usate nei messaggi di posta elettronica o nelle sezioni di chat.

Confesso che crearle mi fece perdere parecchio tempo e arrivai a dichiarare che a causa dell'ambiguità con l'uso di caratteri speciali usati nelle Regex
la cosa era stata molto difficoltosa, ma ora mi sono ricreduto. Infatti, combinando la tecnica delle classi affiancate e con l'uso delle parentesi graffe
la cosa è semplicissima.

Occorre solo mettere le cose nel giusto ordine, aggiungendo,se necessario, delle classi se l'emoticon dovesse essere composta da più di due o tre caratteri,
come invece prevede l'esempio che segue.

Ecco come ho creato la faccina:

# :)) Emoticon Faccina che ride di gusto.

"(\s|^)(\:([-][)]{2,}|[)]{2,}))(\s|\W)        \1Faccina che ride di gusto\4   1       1".

Il primo gruppo si occupa di verificare che la faccina abbia uno (spazio), indicato dal carattere speciale: "s" (barra diagonale rovesciata s minuscola),
oppure che la nostra faccina sia ad inizio riga, indicato dall apice "'" (accento circonflesso).

Il secondo gruppo contiene le classi che compongono la faccina vera e propria. Si può notare il carattere ":" (due punti) per gli occhietti,che è preceduto
dalla "\" (barra diagonale rovesciata) ad indicare che i due punti devono essere trattati alla stregua di semplice carattere e non come carattere speciale,
seguito da una classe contenente il "-" (trattino), il nasino, segue un'altra classe che contiene la ")" (parentesi chiusa), il ghigno sorridente della
nostra emoticon.

Subito seguita da un gruppo di parentesi graffe, ad indicare le ripetizioni ammesse del carattere che le precedono; in questo caso la parentesi chiusa
del
sorriso è ammessa da un minimo di due ripetizioni fino a fine riga perché non è stato indicato il limite massimo dopo la (virgola).

Segue l'alternativa, ossia la stessa faccina, ma senza il nasino questa volta.

Le due alternative sono state raggruppate fra parentesi tonde senza gli occhietti. I due punti che li raffigurano sono stati inseriti una sola volta subito
prima delle due alternative, cioè appena all'inizio e subito all'interno del secondo gruppo di parentesi tonde. Ovviamente se si intende sostituire gli
occhietti raffigurati dai ":" (due punti) con, ad esempio, il ";" (punto e virgola) per indicare la strizzatina d'occhio, non essendo un carattere che
possa essere confuso come un carattere speciale, sarà possibile usarlo senza anteporvi la "\" (barra diagonale rovesciata).

Infine, l'ultimo gruppo di chiusura della Regex controlla che non vi sia appiccicato nient'altro, tranne un eventuale spazio.

Per non generare confusioni con un'altra Regex simile che intercetta la faccina sorridente, cioè quella composta da una sola parentesi tonda chiusa, è
necessario
metterla dopo di quella appena descritta.

Questo per dire che le Regex, in alcune eccezioni non devono essere sempre accodate una all'altra indiscriminatamente; sì, è giusto mantenere per quanto
possibile un ordine alfabetico inanzitutto, ma a volte per evitare conflitti di matching è neccessario dare la precedenza alle Regex che elaborano cose
molto somiglianti fra loro, questo perché i dizionari di Nvda vengono letti e memorizzati dal modulo,l'ho già detto e lo ribadisco, sempre in cascata,
dall'alto verso il basso.

*******

Per eventuali chiarimenti, scrivere a:
Adriano Barbieri.