Hey, Scripting Guy!Stai connesso al tuo tostapane

Microsoft Scripting Guys

Download codice disponibile in: HeyScriptingGuy2008_09,exe(150 KB)

Se questo nostro mondo moderno potesse essere riassunto in due parole, queste sarebbero: stai connesso. Grazie ai telefoni cellulari, non c'è più bisogno di essere in casa per rispondere ad una chiamata; siamo raggiungibili ovunque, in qualsiasi momento del giorno e della notte. (Oh, che… bellezza). Grazie alle connessioni wireless, non c'è bisogno di stare in ufficio per lavorare; ora si può lavorare da casa, dalla spiaggia, da qualsiasi posto vi venga in mente.

Ecco una storia vera: I genitori dell'Editor di script sono andati in campeggio, ma sono stati costretti a rinunciare ad ogni comfort, proprio come i grandi esploratori Lewis e Clark, per dei problemi con la connessione alla rete wireless del camping. E meno male che la tv satellitare funzionava!

Ma il bello deve ancora venire. I dispositivi GPS sono in grado di individuare esattamente dove ci si trova, metro più, metro meno, e, a seconda del dispositivo, possono comunicare ad altri l'esatta posizione. (Il vecchio detto "potrai correre ma non nasconderti" non è mai stato più azzeccato di quanto lo sia oggigiorno). Se solo volesse, lo Scripting Guy che sta scrivendo questo articolo potrebbe farsi telefonare dal suo conto corrente ogni volta che viene emesso un pagamento; allo stesso modo, potrebbe chiedere alla sua automobile un resoconto mensile sullo stato via mail. E come se non bastasse, il tostapane si è offerto di portare a spasso il cane ed annaffiare le piante quando lui è in vacanza.

Beh, OK, forse quest'ultima parte non è del tutto vera - almeno per ora. Ma se solo avesse voluto, lo Scripting Guy avrebbe potuto comprare un tostapane con connessione Internet. Poi avrebbe potuto telefonare al suo tostapane tornando a casa in modo da trovare una fetta di pane tostato caldo caldo ad aspettarlo. Ad essere sinceri, non sappiamo perché dovreste volere del pane tostato caldo appena varcata la soglia di casa. Ma non si sa mai…

Ovviamente, se lo scopo di tutti è essere connessi, non dovrebbe sorprendere il fatto che gli Scripting Guys, che non hanno mai seguito le mode, auspichino una disconnessione. Significa forse che gli Scripting Guys vi consigliano di buttare via il telefono cellulare o il computer portatile? No; persino per gli Scripting Guys sarebbe troppo stupido. Tuttavia, gli piacerebbe che aggiungeste dei recordset disconnessi al vostro arsenale di script. Ma se vuoi comunque buttare via il tuo cellulare o il tuo computer portatile, beh, non saremo noi a fermarti.

Nota Secondo un'indagine condotta da Harris Interactive, il 43% degli americani ha utilizzato un computer portatile durante le vacanze per controllare ed inviare messaggi di posta elettronica per motivi di lavoro. E più del 50% degli americani utilizza il proprio telefono cellulare in vacanza per controllare i messaggi di posta elettronica e/o i messaggi vocali. E ciò non include il 40% degli americani che non vanno affatto in vacanza nell'arco dell'anno.

È ovvio che molti utenti sarebbero felici di aggiungere dei recordset disconnessi al proprio arsenale di script, eccetto per una cosa: non hanno idea di cosa sia un recordset disconnesso. Dunque, qualora il concetto vi sfugga, un recordset disconnesso è (più o meno) una tabella del database che non è legata ad un database effettivo, ma è creata da uno script, esiste soltanto in memoria e scompare nel momento in cui lo script finisce. In altre parole, un recordset disconnesso è una struttura di dati che esiste soltanto per pochi minuti e poi scompare, portando con sé i tuoi dati. Cavolo, sembrerebbe davvero utile, cari Scripting Guys. Grazie per l'aiuto!

Va bene, lo ammettiamo: i recordset disconnessi non sembrano poi così divertenti. E la verità è che non lo sono. Ma possono essere estremamente utili. Come gli scrittori veterani di VBScript ben sanno, il VBScript non ha esattamente le migliori capacità di classificazione dati al mondo. (Beh, a meno che non si consideri una capacità di classificazione dati pari a zero come capacità migliore al mondo). Allo stesso modo, la capacità del VBScript di trattare grandi serie di dati è limitata, nel migliore dei casi. Oltre all'oggetto Dictionary (che limita il lavoro ad elementi che hanno al massimo due proprietà) o all'array (che è strettamente limitato agli elenchi con proprietà singole di dati), beh… non c'è altro.

I recordset disconnessi vi permettono di risolvere entrambi i problemi (e non solo). Avete bisogno di classificare i vostri dati, in particolare quelli in proprietà multipla? Nessun problema; come abbiamo detto, un recordset disconnesso è l'equivalente virtuale di una tabella del database e non c'è niente di più facile al mondo che classificare una tabella del database. (OK, ad essere puntigliosi, è chiaro che non classificare la tabella di un database sia più facile che farlo). E se c'è bisogno di una grande quantità di elementi, elementi in proprietà multipla, di cui tenere traccia? Nessun problema; abbiamo già detto che un recordset disconnesso è l'equivalente virtuale della tabella di un database? Queste informazioni devono essere in qualche modo filtrate oppure è necessario effettuare una ricerca secondo un valore specifico? Oh, se solo ci fosse un modo per utilizzare l'equivalente virtuale di una tabella del database…

Esatto: forse è arrivato il momento di spiegare di cosa stiamo parlando. (Presupponendo di sapere di cosa stiamo parlando). Per i principianti, supponiamo di avere la statistica di baseball illustrata in Figura 1, statistica raccolta dal sito Web MLB.com e memorizzata in un file di valori separati da tabulazione, C: \Scripts\Test.txt.

Figura 1 Statistica memorizzata in un file di valori separati da tabulazione

Player Home Run RBI Media
D. Pedroia 4 28 .276
K. Kouzmanoff 8 25 .269
J. Francouer 7 35 .254
C. Guzman 5 20 .299
F. Sanchez 2 25 .238
I. Suzuki 3 15 .287
J. Hamilton 17 67 .329
I. Kinsler 7 35 .309
M. Ramirez 12 39 .295
A. Gonzalez 17 55 .299

E tutto ciò è cosa buona e giusta, ma supponiamo di voler ottenere un elenco di giocatori divisi per numero di home run effettuati. Un recordset disconnesso può essere utile per qualcosa del genere? Stiamo per scoprirlo; dai un'occhiata alla Figura 2. Sì, ci sono un sacco di codici, non è vero? Ma non preoccuparti; presto scoprirai che can che abbaia non morde.

Figura 2 Un recordset disconnesso

Const ForReading = 1
Const adVarChar = 200
Const MaxCharacters = 255
Const adDouble = 5

Set DataList = CreateObject("ADOR.Recordset")
DataList.Fields.Append "Player", _
  adVarChar, MaxCharacters
DataList.Fields.Append "HomeRuns", adDouble
DataList.Open

Set objFSO = _
  CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile _
  ("C:\Scripts\Test.txt", ForReading)

objFile.SkipLine

Do Until objFile.AtEndOfStream
    strStats = objFile.ReadLine
    arrStats = Split(strStats, vbTab)

    DataList.AddNew
    DataList("Player") = arrStats(0)
    DataList("HomeRuns") = arrStats(1)
    DataList.Update
Loop

objFile.Close

DataList.MoveFirst

Do Until DataList.EOF
    Wscript.Echo _
        DataList.Fields.Item("Player") & _
        vbTab & _
        DataList.Fields.Item("HomeRuns")
    DataList.MoveNext
Loop

Per iniziare, definiamo quattro costanti:

  • ForReading. Utilizziamo questa costante quando apriamo e leggiamo il file di testo.
  • AdVarChar. Questa è una costante ADO standard per creare un campo che utilizza il tipo di dati Variant.
  • MaxCharacters. Questa è una costante ADO standard per indicare il numero massimo di caratteri (in questo caso, 255) che un campo Variant può ammettere.
  • adDouble. Un'ultima costante ADO, per creare un campo che utilizza un tipo di dati double (numerici).

Dopo aver definito le costanti, troviamo questo blocco di codice:

Set DataList = CreateObject _
    ("ADOR.Recordset")
DataList.Fields.Append "Player", _
    adVarChar, MaxCharacters
DataList.Fields.Append "HomeRuns", _
    adDouble
DataList.Open

Questa è la parte dello script dove stabiliamo e configuriamo concretamente il recordset disconnesso. Per realizzare questa attività, la prima cosa che facciamo è creare un'istanza dell'ADOR.Oggetto recordset; inutile a dirsi, ciò consente di creare la tabella del database virtuale (ossia, il nostro recordset disconnesso).

Utilizziamo poi questa riga di codice (ed il metodo Append) per aggiungere un campo nuovo al recordset:

DataList.Fields.Append "Player", adVarChar, MaxCharacters

Come puoi vedere, non c'è niente di elaborato qui: Chiamiamo semplicemente il metodo Append seguito da tre parametri:

  • Il nome del campo (Players).
  • Il tipo di dati per il campo (adVarChar).
  • Il numero massimo di caratteri che possono essere memorizzati nel campo (MaxCharacters).

Dopo aver aggiunto il campo Players, possiamo aggiungere un secondo campo: HomeRuns, che ha un tipo di dati numerico (adDouble). Una volta finito con questa attività, chiamiamo il metodo Open per dichiarare il nostro recordset aperto e pronto a lavorare.

Poi creiamo una richiesta di Script.FileSystemObject ed apriamo il file: C:\Scripts\Test.txt. Questa porzione dello script in realtà non ha niente a che fare col recordset disconnesso; è lì solo perché dobbiamo recuperare i dati da un file di testo. La prima riga nel file di testo contiene le nostre informazioni di intestazione:

Player     Home Runs     RBI        Average

Non abbiamo bisogno di queste informazioni per il recordset, dunque la prima cosa da fare dopo aver aperto il file è chiamare il metodo SkipLine per ignorare questa prima riga:

objFile.SkipLine

Ora che siamo passati alla prima riga contenente dati effettivi, prepariamo un loop Do Until progettato per leggere il resto del file riga per riga. Ogni volta che leggiamo una riga dal file, memorizziamo quel valore in una variabile denominata strLine, utilizzeremo poi la funzione Split per convertire quella riga in un array di valori (dividendo la riga ogni volta che incontriamo una scheda):

arrStats = Split(strStats, vbTab)

Lo ammettiamo, è solo una panoramica veloce, ma crediamo che ormai siate abbastanza bravi a recuperare informazioni dai file di testo. Per farla breve, al primo loop l'array, arrStats, conterrà gli elementi in Figura 3.

Figura 3 Contenuti dell'array

Numero elemento Nome elemento
0 D. Pedroia
1 4
2 28
3 .276

Ora inizia il divertimento:

DataList.AddNew
DataList("Player") = arrStats(0)
DataList("HomeRuns") = arrStats(1)
DataList.Update

Qui aggiungiamo le informazioni per il giocatore 1 (D. Pedroia) al recordset disconnesso. Per aggiungere un record al recordset, iniziamo chiamando il metodo AddNew; questo crea un record nuovo, vuoto su cui lavorare. Utilizziamo le due righe seguenti di codice per assegnare i valori a due campi di recordset (Player e HomeRuns), poi chiamiamo il metodo Update per scrivere ufficialmente quel record sul recordset. E poi si ricomincia il loop ancora una volta, ripetendo il processo con la riga successiva—il prossimo giocatore—del file di testo. Visto? Anche se avessimo molti codici, sarebbe tutto piuttosto semplice e diretto.

Dunque che succede dopo che tutti i giocatori sono stati aggiunti al recordset? Beh, dopo aver chiuso il file di testo, eseguiamo questo blocco di codice:

DataList.MoveFirst

Do Until DataList.EOF
  Wscript.Echo _
    DataList.Fields.Item("Player") & _
    vbTab & _
    DataList.Fields.Item("HomeRuns")
  DataList.MoveNext
Loop

Nella riga 1 utilizziamo il metodo MoveFirst per posizionare il cursore all'inizio del recordset; in caso contrario, correremmo il rischio di mostrare soltanto alcuni dei dati nel recordset. Avvieremo poi un loop Do Until che continuerà fino ad esaurimento dati (cioè, finché la proprietà EOF, end of file, del recordset non sarà True).

All'interno del loop, tutto ciò che facciamo è ripetere i valori dei campi Player e HomeRuns (notare la sintassi piuttosto bizzarra usata per indicare un campo particolare: DataList.Fields.Item("Player"). E poi chiamiamo semplicemente il metodo Move-Next per passare al record successivo nel recordset.

Inutile a dirsi, era davvero facile. Alla fine dei giochi dovremmo ottenere quanto segue:

D Pedroia       4
K Kouzmanoff    8
J Francouer     7
C Guzman        5
F Sanchez       2
I Suzuki        3
J Hamilton      17
I Kinsler       7
M Ramirez       12
A Gonzalez      17

Come potete vedere, è... beh, se ci pensi bene, non è che sia tutta questa meraviglia, eh? Certo, abbiamo ottenuto i nomi dei giocatori e gli home run totali, ma non abbiamo ottenuto questi totali in un ordine specifico. Mannaggia! Perché il recordset disconnesso non ha classificato i dati al posto nostro?

In realtà c'è un'ottima ragione: non abbiamo detto al recordset che campo volevamo classificare. Ma rimediare è abbastanza facile: basta modificare lo script per aggiungere informazioni di classificazione appena prima della chiamata del metodo MoveFirst. In altre parole, fate in modo che quella parte di script risulti così:

DataList.Sort = "HomeRuns"
DataList.MoveFirst

Ovviamente non c'è nessun trucco qui; assegniamo semplicemente il campo HomeRuns alla proprietà di Ordinamento. Ora date un'occhiata all'output che si ottiene quando eseguiamo lo script:

F Sanchez       2
I Suzuki        3
D Pedroia       4
C Guzman        5
J Francouer     7
I Kinsler       7
K Kouzmanoff    8
M Ramirez       12
J Hamilton      17
A Gonzalez      17

Molto meglio. Fatta eccezione per un punto: di solito gli home run totali sono elencati in ordine decrescente, col giocatore che ha realizzato il maggior numero di home run al primo posto. Esiste un modo per classificare un recordset disconnesso in ordine decrescente?

Ma certo; tutto ciò che si deve fare è aggiungere l'utilissimo parametro DESC, così:

DataList.Sort = "HomeRuns DESC"

E che cosa farà per noi il parametro DESC? Esatto:

A Gonzalez      17
J Hamilton      17
M Ramirez       12
K Kouzmanoff    8
I Kinsler       7
J Francouer     7
C Guzman        5
D Pedroia       4
I Suzuki        3
F Sanchez       2

A proposito, è perfettamente legale classificare proprietà multiple; tutto ciò che devi fare è assegnare ad ogni proprietà un ordinamento di dati. Ad esempio, supponi di voler classificare prima gli home run e poi gli RBI. Nessun problema; questo comando lo farà al posto tuo:

DataList.Sort = "HomeRuns DESC, RBI DESC"

Prova e lo vedrai con i tuoi occhi. Non sarà divertente come controllare la posta elettronica in vacanza, ma quasi.

Nota Ricorda che non è possibile classificare secondo un campo che non è stato aggiunto al recordset. Cosa significa? Significa che prima di aggiungere una proprietà come gli RBI all'istruzione di ordinamento, è necessario aggiungere queste righe allo script nelle posizioni appropriate:

DataList.Fields.Append "RBI", adDouble

DataList("RBI") = arrStats(2)

E se vuoi vedere il risultato, devi anche modificare l'istruzione Wscript.Eco:

Wscript.Echo _
  DataList.Fields.Item("Player") & _
  vbTab & _
  DataList.Fields.Item("HomeRuns") & _
  vbTab & DataList.Fields.Item("RBI")

Vediamo, cos'altro si può fare con i recordset disconnessi? Oh, ecco qualcosa. Supponiamo di aver recuperato tutte le informazioni per tutti i giocatori ed aver classificato quei dati secondo la media di battuta. (Fra le altre cose, ciò significa che dobbiamo modificare lo script originale per creare i campi denominati RBI e Media-Battuta.) L'output è simile al seguente:

J Hamilton      17      67      0.329
I Kinsler       7       35      0.309
A Gonzalez      17      55      0.304
C Guzman        5       20      0.299
M Ramirez       12      39      0.295
I Suzuki        3       15      0.287
D Pedroia       4       28      0.276
K Kouzmanoff    8       25      0.269
J Francouer     7       35      0.254
F Sanchez       2       25      0.238

Benissimo, ma se volessimo solo un elenco dei battitori da .300 o oltre? Come possiamo limitare i dati visualizzati ai giocatori che soddisfano alcuni criteri specifici? Beh, un modo è quello di assegnare un Filtro al recordset:

DataList.Filter = "BattingAverage >= .300"

Un filtro di recordset serve lo stesso scopo generale di una query sul database: Fornisce un meccanismo per limitare i dati ottenuti ad un sottoinsieme di tutti i record nel recordset. In questo caso chiediamo semplicemente al Filtro di eliminare tutti i record eccetto quelli in cui la Media-Battuta ha un valore superiore o uguale a .300 E indovinate un po'? Il filtro farà esattamente ciò che gli chiediamo:

J Hamilton      17      67      0.329
I Kinsler       7       35      0.309
A Gonzalez      17      55      0.304

Se solo i nostri figli fossero così ricettivi, eh?

Comunque, puoi utilizzare criteri multipli in un solo filtro. Ad esempio, questo comando limita i dati risultanti ai record in cui il campo MediaBattuta è maggiore o uguale a .300 ed il campo HomeRuns è maggiore di 10:

DataList.Filter = _
  "BattingAverage >= .300 AND HomeRuns > 10"

Per contro, questo filtro limita i dati risultanti ai record in cui il campo MediaBattuta è maggiore o uguale a .300 o il campo HomeRuns è maggiore di 10:

DataList.Filter = "BattingAverage >= .300 OR HomeRuns > 10"

Prova con entrambi e noterai la differenza. E che diamine: solo per divertirti, ecco un altro filtro che puoi provare:

DataList.Filter = "Player LIKE 'I*'"

Andando avanti, potrai utilizzare anche dei caratteri jolly nei filtri. Per fare questo, utilizza l'operatore LIKE (invece del segno di uguale) e poi utilizza l'asterisco come faresti per eseguire un comando MS-DOS ® come dir C: \Scripts\*.txt. Nell'esempio precedente, dovremmo inserire un elenco di giocatori il cui nome inizia con la lettera I; poiché la sintassi che abbiamo impiegato dice: "Mostrami un elenco di tutti i record dove il valore del campo Player inizia con una I seguita da, beh, qualsiasi cosa". Ora prova, beh, OK; ormai hai imparato la procedura.

Comunque, non sarai ancora fermo alle medie di battuta tipo .309. (In genere le medie di battuta sono espresse senza lo 0, in questo caso .309, ad esempio). Ma va bene; puoi semplicemente utilizzare la funzione FormatNumber per formattare la media di battuta in qualsiasi maniera antiquata tu voglia:

FormatNumber (DataList.Fields.Item("BattingAverage"), 3, 0)

Inserisci semplicemente questa funzione nell'istruzione Wscript.Echo quando visualizzi il numero (o altrimenti, puoi assegnare l'output a una variabile e inserire la variabile nell'istruzione Echo) :

Wscript.Echo _
  DataList.Fields.Item("Player") & _
  vbTab & _
  DataList.Fields.Item("HomeRuns") & _
  vbTab & DataList.Fields.Item("RBI") & _
  vbTab & _
  FormatNumber _
  (DataList.Fields.Item("BattingAverage"), _
   3, 0)

Roba divertente, eh?

Ma purtroppo, sembra che il tempo per questo mese sia agli sgoccioli. Per concludere, vorrei solo dire che, scusate, sta squillando il telefono.

Comunque, volevamo sottolineare che—oh, grande, ora squilla il cellulare. Ed abbiamo appena ricevuto un messaggio di posta elettronica dal tostapane. Roba seria: il toast caldo è pronto, ci vuoi il burro o la marmellata? Dobbiamo andare, al prossimo mese!

Il rompicapo di script del Dottor Scripto

La sfida mensile che verifica non solo l'abilità nella risoluzione dei puzzle, ma anche le competenze di scripting.

Settembre 2008: Ricerca di script

Ecco qui una semplice (o forse non così semplice) ricerca di parole. Trova tutte le funzioni VBScript e le istruzioni nell'elenco. Ma ecco la sorpresa: le lettere che rimangono formano una parola nascosta, che per puro caso, indovina un po', è un cdmlet Windows PowerShell™!

Elenco di parole: Abs, Array, Atn, CCur, CLng, CInt, DateValue, Day, Dim, Else, Exp, Fix, InStr, IsEmpty, IsObject, Join, Len, Log, Loop, LTrim, Mid, Month, MsgBox, Now, Oct, Replace, Set, Sin, Space, Split, Sqr, StrComp, String, Timer, TimeValue, WeekdayName.

fig08.gif

RISPOSTA:

Il rompicapo di script del Dottor Scripto

Risposta: Settembre 2008: Ricerca di script

fig08.gif

Gli Scripting Guys lavorano per, o meglio, sono stipendiati da Microsoft. Quando non si dedicano al baseball (o a varie altre attività) da giocatori, allenatori o semplici spettatori, gestiscono il TechNet Script Center. È possibile consultarne la pagina all'indirizzo www.scriptingguys.com.