Ei, Equipe de Scripts!Conecte-se à sua torradeira

A Equipe de Scripts da Microsoft

Código disponível para download em: HeyScriptingGuy2008_09.exe(150 KB)

Se há algo que agrega a este nosso mundo moderno são estas palavras: conecte-se. Graças aos telefones celulares, você não precisa mais estar em casa para que as pessoas liguem; elas podem entrar em contato independentemente de onde você esteja, a qualquer hora do dia ou da noite. (Xi... Bem, legal.) Graças à computação sem fio, você não precisa mais estar no escritório para realizar o trabalho; agora é possível trabalhar em casa, na praia, em qualquer lugar imaginável.

História real: recentemente, os pais do Editor de Scripts foram acampar, mas foram forçados a enfrentar a situação – assim como os grandes desbravadores Lewis e Clark – quando tiveram problemas de conexão com a rede sem fio do acampamento. Graças a Deus, pelo menos a TV por satélite estava funcionando!

E não chegamos à metade da história. Os dispositivos GPS permitem saber exatamente onde você está, em questão de metros; dependendo do dispositivo, eles também podem permitir que outras pessoas saibam exatamente onde você está. (O velho ditado "você pode fugir, mas não pode se esconder" nunca foi tão real como atualmente.) Se quisesse, o membro da Equipe de Scripts quem escreve esta coluna poderia pedir ao banco para avisá-lo sempre que um cheque fosse compensado; da mesma forma, ele poderia receber relatórios de status mensais sobre seu carro por email. Se isso não bastasse, a torradeira se ofereceu para levar o cachorro para passear e regar as plantas sempre que ele estiver em férias.

Tudo bem, essa última parte talvez não tenha acontecido – ainda. Mas se quisesse, o membro da Equipe de Scripts poderia comprar uma torradeira habilitada para Internet. Assim, ele poderia ligar para a torradeira a caminho de casa e ter torradas quentinhas à sua espera assim que entrasse pela porta. Sinceramente, não sabemos por que você queria ter torradas quentinhas à sua espera assim que entrasse pela porta. Mas se quisesse …

É claro que se o objetivo de todos é se manter conectado, não deve ser nenhuma surpresa que a Equipe de Scripts – que tem sido uma escrava da modernidade – defenda que você se mantenha mais desconectado. Isso significa que a Equipe de Scripts recomende jogar fora o telefone celular ou o computador laptop? Não, a Equipe de Scripts é mais esperta que isso. No entanto, o que estamos defendendo é que você adicione conjuntos de registros desconectados ao arsenal de scripts. Mas se quiser jogar fora o telefone celular ou o computador laptop, bem, não o impediremos.

Observação De acordo com uma pesquisa realizada pela Harris Interactive, 43 por cento dos norte-americanos usaram um computador laptop durante as férias para verificar e enviar email relacionado a trabalho. E mais de 50 por cento deles usaram os telefones celulares durante as férias para verificar email e/ou caixa postal. E isso não inclui os 40 por cento que não tiram férias ao longo de um ano.

Não é preciso dizer que muitas pessoas adicionariam tranqüilamente conjuntos de registros desconectados ao arsenal de scripts, não fosse por uma coisa: elas não fazem idéia do que é um conjunto de registros desconectado. Bem, caso você não esteja familiarizado com o conceito, um conjunto de registros desconectado é (mais ou menos) uma tabela de banco de dados que não está vinculada a um banco de dados real; na verdade, ele é criado por um script, existe apenas na memória e desaparece no momento em que o script é encerrado. Em outras palavras, um conjunto de registros é uma estrutura de dados produzida que só existe durante alguns minutos e, em seguida, desaparece, levando os dados com ela. Deus do céu, isso parece muito útil, Equipe de Scripts. Agradecemos a ajuda!

Certo, admitimos: conjuntos de registros desconectados não parecem tão incríveis assim. E, verdade seja dita, não são. Mas eles podem ser extremamente úteis. Como sabem muito bem todos os programadores em VBScript, o VBScript não conta exatamente com os melhores recursos para classificação de dados do mundo. (Bem, não a menos que você considere a ausência de recursos para classificação de dados os melhores do mundo.) Da mesma forma, a possibilidade do VBScript de lidar com grandes conjuntos de dados é, na melhor das hipóteses, limitada. Fora do objeto Dictionary (que se restringe ao trabalho com itens com, no máximo, duas propriedades) ou da matriz (que é amplamente limitada a listas de dados de propriedade única), bem … é só isso.

O conjunto de registros desconectado permite resolver esses dois problemas (e muito mais). Precisa classificar os dados, especialmente dados com várias propriedades? Sem problemas. Como dissemos, um conjunto de registros desconectado é o equivalente virtual de uma tabela de banco de dados, e não há nada mais fácil no mundo do que classificar uma tabela de banco de dados. (Tudo bem, caso você queira ser mais exigente quanto a isso, supomos que deixar de classificar uma tabela de banco de dados seja mais fácil do que classificar uma.) Ou talvez você tenha um grande conjunto de itens, itens com várias propriedades, que precisa acompanhar? Sem problemas. Não mencionamos que um conjunto de registros desconectado é o equivalente virtual de uma tabela de banco de dados? Você precisa filtrar essas informações de alguma maneira, ou quem sabe pesquisar esses dados em busca de um valor específico? Xi... só se houvesse uma forma de usar o equivalente virtual de uma tabela de banco de dados…

Uma boa questão: talvez se trate do tempo para mostrar o que estamos falando. (Pressupondo que pelo menos saibamos do que estamos falando.) Para iniciantes, digamos que tenhamos as estatísticas de beisebol mostradas na Figura 1, estatísticas obtidas no site MLB.com e armazenadas em um arquivo cujos valores são separados por tabulações, C:\Scripts\Test.txt.

Figura 1 Estatísticas armazenadas em um arquivo de valores separados por tabulações

Jogador Home runs RBI Média
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

Tudo muito bom, tudo muito bem, mas suponhamos que queiramos mesmo mostrar essa lista de jogadores classificada de acordo com o número de home runs conseguidos. Um conjunto de registros desconectado nos ajuda em algo desse tipo? Estamos prestes a descobrir. Veja a Figura 2. Sim, há muitos códigos aqui, não é mesmo? Mas não se preocupe. Como você verá daqui a pouco, o latido é bem pior do que a mordida.

Figura 2 Um conjunto de registros desconectado

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

Para começar, definimos quatro constantes:

  • ForReading. Usaremos essa constante ao abrir e ler o arquivo de texto.
  • adVarChar. Trata-se de uma constante do ADO padrão para criar um campo que usa o tipo de dados Variant.
  • MaxCharacters. Trata-se de uma constante ADO usada para indicar o número máximo de caracteres (nesse caso, 255) que um campo Variant pode armazenar.
  • adDouble. Uma constante do ADO final para criar um campo que usa o tipo de dados double (numérico).

Depois de definirmos as constantes, encontramos este bloco de código:

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

Esta é a parte do script em que efetivamente definimos e configuramos o nosso conjunto de registros desconectado. Para realizar essa tarefa, a primeira coisa que fazemos é criar uma instância do objeto ADOR.Recordset; nem é preciso dizer que isso cria a nossa tabela de banco de dados virtual (ou seja, o nosso conjunto de registros desconectado).

Em seguida, usamos essa linha de código (e o método Append) para adicionar um novo campo ao conjunto de registros:

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

Como se pode ver, não há nada demais acontecendo aqui: apenas chamamos o método Append seguido de três parâmetros:

  • O nome do campo (Players).
  • O tipo de dados do campo (adVarChar).
  • O número máximo de caracteres que podem ser armazenados no campo (MaxCharacters).

Depois de adicionarmos o campo Players, podemos adicionar um segundo: HomeRuns, com um tipo de dados (adDouble) numérico. Quando concluirmos essa tarefa, chamamos o método Open para declarar o nosso conjunto de registros aberto e pronto.

Em seguida, criamos uma instância de Scripting.FileSystemObject e abrimos o arquivo C:\Scripts\Test.txt. Na verdade, essa parte do script não tem nada a ver com o conjunto de registros desconectado; só está aí porque precisamos recuperar dados de um arquivo de texto. A primeira linha do arquivo de texto contém as informações do nosso cabeçalho:

Player     Home Runs     RBI        Average

Como não precisamos dessas informações para o nosso conjunto de registros, a primeira coisa que fazemos depois de abrir o arquivo é chamar o método SkipLine para ignorar essa primeira linha:

objFile.SkipLine

Agora que passamos à primeira linha com dados reais nela, configuramos um loop Do Until projetado para nos permitir ler o restante do arquivo linha por linha. Sempre que lemos uma linha do arquivo, armazenamos esse valor em uma variável chamada strLine e, em seguida, usamos a função Split para converter essa linha em uma matriz de valores (dividindo a linha sempre que encontramos uma tabulação):

arrStats = Split(strStats, vbTab)

De fato, essa é uma visão geral rápida, mas esperamos que, por enquanto, a maioria de vocês seja muito boa na recuperação de informações dos arquivos de texto. Para encurtar a história, a primeira execução do loop na matriz, arrStats, conterá os itens da Figura 3.

Figura 3 Conteúdo da matriz

Número do item Nome do item
0 D Pedroia
1 4
2 28
3 .276

Agora estamos prontos para alguma diversão:

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

Aqui estamos adicionando as informações referentes ao jogador 1 (D Pedroia) ao conjunto de registros desconectado. Para adicionar um registro ao conjunto, começamos chamando o método AddNew; isso cria um novo registro, em branco, para nós trabalharmos. Usamos as duas próximas linhas de código para atribuir valores aos dois campos do conjunto de registros (Player e HomeRuns) e, em seguida, chamamos o método Update para gravar oficialmente o registro no conjunto. E voltando à parte superior do loop mais uma vez, onde repetimos o processo com a próxima linha – o próximo jogador – no arquivo de texto. Viu? Pode haver uma infinidade de códigos aqui, mas é tudo muito simples e direto.

E o que acontece depois que todos os jogadores são adicionados ao conjunto de registros? Bem, depois de fecharmos o arquivo de texto, executamos este bloco de código:

DataList.MoveFirst

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

Na linha 1 usamos o método MoveFirst para posicionar o cursor no início do conjunto de registros; se não fizermos isso, correremos o risco de mostrar apenas alguns dos dados do conjunto. Em seguida, configuramos um loop Do Until que continuará sendo executado até os dados acabarem (ou seja, até a propriedade EOF – fim do arquivo – ser True).

Dentro desse loop, tudo o que fazemos é recuperar os valores dos campos Player e HomeRuns (observe a sintaxe um pouco estranha usada para indicar um determinado campo): DataList.Fields.Item("Player"). Depois, apenas chamamos o método MoveNext para passar ao próximo registro do conjunto.

Nem é preciso dizer que isso foi bem fácil. Uma vez tudo resolvido e pronto, devemos ter o seguinte:

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

Como se pode ver, isso – bem, pensando melhor, nem tudo está tão bom assim, está? Certo, conseguimos os nomes dos jogadores e os totais de home runs, mas não esses totais de home runs classificados. Droga! Por que o conjunto de registros não classificou os dados para nós?

Na verdade, há um bom motivo: não informamos ao conjunto de registros qual campo queríamos classificar. Mas isso é bem fácil de se corrigir: basta modificar o script para adicionar informações de classificação pouco antes da chamada para o método MoveFirst. Em outras palavras, faça essa parte do script ficar assim:

DataList.Sort = "HomeRuns"
DataList.MoveFirst

Obviamente, não há nenhum truque envolvido aqui; apenas atribuímos o campo HomeRuns à propriedade Sort. Agora observe a saída que obtemos quando executamos o 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

Muito melhor. Muito bem, exceto por um aspecto: normalmente, os totais de home runs são listados na ordem decrescente, com o jogador com mais home runs aparecendo em primeiro lugar. Existe alguma forma de classificar um conjunto de registros desconectado na ordem decrescente?

É claro que há; tudo o que temos a fazer é acrescentar o útil parâmetro DESC da seguinte forma:

DataList.Sort = "HomeRuns DESC"

E o que o parâmetro DESC faz para nós? Isso mesmo:

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

Por acaso, é perfeitamente legal classificar várias propriedades; tudo o que você precisa fazer é atribuir cada uma delas à ordem de classificação. Por exemplo, suponhamos que você queira classificar primeiro os home runs e, em seguida, os RBIs. Sem problemas. O comando fará a mágica:

DataList.Sort = "HomeRuns DESC, RBI DESC"

Faça uma tentativa e veja com seus próprios olhos. Não é tão legal como verificar o email durante as férias, mas chega perto.

Observação Lembre-se de que não é possível classificar um campo que não tenha sido adicionado ao conjunto de registros. O que isso significa? Significa que, para adicionar uma propriedade como, por exemplo, RBI à instrução Sort, você precisa adicionar estas linhas ao script nos locais apropriados:

DataList.Fields.Append "RBI", adDouble

DataList("RBI") = arrStats(2)

E se quiser observar a saída, você também precisará modificar a instrução Wscript.Echo:

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

Vejamos, o que mais podemos fazer com conjuntos de registros desconectados? Ah, aqui está. Suponhamos que tenhamos recuperado todas as informações referentes a todos os jogadores e, em seguida, classificado esses dados de acordo com a média de rebatidas. (Dentre outras coisas, isso significa que precisamos modificar o nosso script original para criar campos chamados RBI e BattingAverage.) A saída é semelhante a esta:

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

Tudo bem, mas e se quiséssemos uma lista apenas dos jogadores com .300 de rebatidas ou mais? Como é possível limitar os dados exibidos apenas aos jogadores que correspondam a critérios especificados? Bem, uma forma é atribuir um filtro ao conjunto de registros:

DataList.Filter = "BattingAverage >= .300"

Um filtro do conjunto de registros tem o mesmo objetivo geral de uma consulta ao banco de dados: proporciona um mecanismo para limitar dados retornados a um subconjunto de todos os registros do conjunto. Nesse caso, estamos apenas pedindo a Filter para eliminar todos os registros, exceto aqueles nos quais o campo BattingAverage apresenta um valor maior que ou igual a .300. E adivinhe só: Filter fará exatamente aquilo que você pediu:

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

Ah se nossos filhos fossem assim, hein?

A propósito, é possível usar vários critérios em um único filtro. Por exemplo, esse comando limita os dados retornados a registros nos quais o campo BattingAverage é maior que ou igual a .300 e o campo HomeRuns, maior que 10:

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

Por outro, esse filtro limita os dados a registros nos quais o campo BattingAverage é maior que ou igual a .300 ou o campo HomeRuns, maior que 10:

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

Faça um teste com ambos, e você verá qual é a diferença. E que burrice a minha: como diversão, eis outro filtro que é possível experimentar:

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

Acontece que também é possível usar curingas nos filtros. Para isso, use o operador LIKE (e não o sinal de igual) e, em seguida, use o asterisco da mesma forma com que faria se estivesse executando um comando do MS-DOS® como dir C:\Scripts\*.txt. No exemplo anterior, devemos receber uma lista dos jogadores cujos nomes começam pela letra I; isso porque a sintaxe que empregamos diz: "me mostre uma lista de todos os registros nos quais o valor do campo Player comece por I e seja seguido, bem, por qualquer coisa". Dê um – bem, OK; agora você já conhece bem a rotina.

A propósito, você também não fica limitado a médias de rebatida como 0,309. (As médias de rebatida costumam ser expressadas sem o 0, à esquerda .309, por exemplo.) Mas tudo bem; basta usar a função FormatNumber para formatar a média de rebatida da forma que quiser:

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

Basta incluir essa função na instrução Wscript.Echo ao exibir o número (ou atribuir a saída a uma variável e colocá-la na instrução 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)

Legal, não?

Mas, infelizmente, acabou o espaço que tínhamos para este mês. Resumindo, gostaríamos de dizer que – desculpe, o telefone está tocando.

Então, queremos observar que – ai, que ótimo, agora é o telefone celular que está tocando. E acabamos de receber um email da torradeira. Importante: a nossa torrada está pronta e não sabemos que queremos com manteiga ou geléia... Temos que nos despedir, mas nos veremos no mês que vem!

O desafio de script do Dr. Scripto

O desafio mensal que testa não apenas sua habilidade de resolver quebra-cabeça, mas também de criar scripts.

Setembro de 2008: Pesquisa sobre scripts

Eis um caça-palavras simples (talvez nem tanto assim). Encontre todas as funções e instruções VBScript da lista. Mas eis um detalhe: as letras restantes formarão uma palavra oculta, que é nada mais, nada menos do que – você adivinhou! – um cmdlet do Windows PowerShell™!

Lista de palavras: 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

RESPOSTA:

O desafio de script do Dr. Scripto

Resposta: Setembro de 2008: Pesquisa sobre scripts

puzzle_answer.gif

A Equipe de Scripts trabalha na – quer dizer, é contratada pela – Microsoft. Quando não estão jogando/treinando/assistindo beisebol (e praticando diversas outras atividades), eles administram o Script Center da TechNet. Confira no site www.scriptingguys.com.