Hey, Scripting Guy!Permanezca atento a su tostador

Los Scripting Guys de Microsoft

Descarga de código disponible en: HeyScriptingGuy2008_09.exe(150 KB)

Si hay algo que resume nuestro mundo moderno son estas palabras: permanezca conectado a Internet. Gracias a los teléfonos móviles, ya no necesita estar en casa para que la gente lo llame; lo pueden encontrar esté donde esté, en cualquier momento del día o la noche. (Ah, qué... bien.) Gracias a la informática inalámbrica, no tiene que estar en la oficina para realizar su trabajo; ahora puede trabajar desde casa, desde la playa o prácticamente desde cualquier parte que pueda imaginar.

Una historia real: recientemente, los padres del editor de scripting se fueron de camping, pero se vieron forzados a vivir sin comodidades, como los grandes exploradores Lewis y Clark, cuando tuvieron problemas para conectarse a la red inalámbrica del camping. Menos mal que la televisión vía satélite todavía funcionaba.

Y eso sólo es el principio. Los dispositivos GPS permiten saber exactamente la posición en que uno se encuentra, en cuestión de centímetros; en función del dispositivo, también pueden hacer que otras personas conozcan exactamente la posición de uno mismo. (El viejo dicho de "puedes correr pero no ocultarte" nunca antes había sido tan cierto.) Si lo deseara, el Scripting Guy que escribe esta columna podría hacer que su cuenta corriente le llamara por teléfono cada vez que se liquide un cheque; igualmente, podría hacer que su automóvil le envíe informes de estado mensuales por correo electrónico. Por si esto fuera poco, el tostador se ha ofrecido a sacar el perro a pasear y regar las plantas cuando él se vaya de vacaciones.

Bueno, de acuerdo, quizás lo último no ha sucedido, todavía. Pero si quisiera, el Scripting Guy podría comprar un tostador con conexión a Internet. Así podría llamar al tostador de camino a casa y tener tostadas bien calientes esperándolo a su llegada. Para ser honestos, no sabemos por qué querría tener tostadas bien calientes esperándolo en casa. Pero si así fuera…

Por supuesto, si el objetivo de todos es permanecer conectados, no debe sorprender que los Scripting Guys, que nunca han sido esclavos de lo moderno, sean partidarios de llegar a estar más desconectados. ¿Esto significa que los Scripting Guys recomiendan que tire el teléfono móvil o el equipo portátil? No, hasta los Scripting Guys son más inteligentes que eso. Sin embargo, lo que recomiendan es que agregue un conjunto de registros desconectado al arsenal de scripts. Pero si desea tirar su teléfono móvil o equipo portátil, está bien, no le detendremos.

Nota Según un estudio realizado por Harris Interactive, el 43 por ciento de los norteamericanos ha usado un equipo portátil mientras estaba de vacaciones para comprobar y enviar correo electrónico relacionado con el trabajo. Y más del 50 por ciento de los norteamericanos usa sus teléfonos móviles mientras están de vacaciones para comprobar su correo electrónico y/o correo de voz. Y eso no incluye el 40 por ciento de los norteamericanos que no toman vacaciones a lo largo del año.

No hace falta decir que a muchos les gustaría agregar conjuntos de registros desconectados a su arsenal de scripts, excepto por una sola cosa: que no tienen la menor idea de lo que es un conjunto de registros desconectado. Bueno, en caso de que no esté familiarizado con el concepto, un conjunto de registros desconectado es (más o menos) una tabla de la base de datos que no está vinculada a una base de datos real; en lugar de eso, ha sido creada por un script, sólo existe en memoria y desaparece en el momento en que finaliza el script. Es decir, un conjunto de registros desconectado es una estructura de datos ficticia que existe durante algunos minutos y luego desaparece, llevándose los datos. Caramba, eso suena realmente útil, Scripting Guys. ¡Gracias por la ayuda!

De acuerdo, lo admitimos, los conjuntos de registros desconectados no suenan muy interesantes. Y la verdad es que no lo son. Pero pueden resultar muy útiles. Como los escritores veteranos de VBScript bien saben, VBScript no tiene precisamente las mejores capacidades de ordenación de datos del mundo. (Bueno, salvo que piense que ninguna de las capacidades de ordenación de datos es la mejor del mundo.) Asimismo, la capacidad de VBScript para trabajar con grandes conjuntos de datos es, en el mejor de los casos, limitada. Fuera del objeto Dictionary (que lo limita a trabajar con elementos que tienen a lo sumo dos propiedades) o el objeto array (que está en gran parte limitado a listas de datos de propiedad única), bueno... eso es todo.

El conjunto de registros desconectado le permite ocuparse de esos problemas (y hacer muchas otras cosas). ¿Necesita ordenar sus datos, especialmente datos con propiedades múltiples? Ningún problema; como dijimos, un conjunto de registros desconectado es el equivalente virtual de una tabla de la base de datos, y no hay nada más fácil en este mundo que ordenar una tabla. (Vale, si quiere ponerse quisquilloso, suponemos que no ordenar una tabla de la base de datos es más fácil que ordenar una.) ¿O quizás ha obtenido un gran conjunto de elementos, con propiedades múltiples, y desea realizar un seguimiento? Ningún problema, ¿no dijimos que un conjunto de registros desconectado es el equivalente virtual de una tabla de la base de datos? ¿Necesita filtrar esa información de alguna manera o quizás buscar un valor específico en los datos? Ah, si sólo hubiera alguna manera de usar el equivalente virtual de una tabla de la base de datos...

Buena observación: puede que ya sea hora de mostrarle de qué hablamos (presuponiendo que al menos sabemos de qué estamos hablando). Para comenzar, digamos que tenemos las estadísticas de béisbol que se muestran en la figura 1 que hemos bajado del sitio web MLB.com y almacenado en un archivo de valores separados por comas o tabulaciones, C:\Scripts\Test.txt.

Figura 1 Estadísticas almacenadas en un archivo de valores separados por tabulaciones

Jugador Cuadrangulares Carreras impulsadas Promedio
D Pedroia 4 28 .276
K Kouzmanoff 8 25 .269
J Francouer 7 35 .254
C Guzmán 5 20 .299
F Sánchez 2 25 .238
I Suzuki 3 15 .287
J Hamilton 17 67 .329
I Kinsler 7 35 .309
M Ramírez 12 39 .295
A González 17 55 .299

Todo eso está bien y es bueno, pero supongamos que lo que en realidad deseamos hacer es mostrar esta lista de jugadores ordenada por la cantidad de cuadrangulares que lograron. ¿Un conjunto de registros desconectado nos puede ayudar con algo así? Estamos a punto de averiguarlo, consulte la figura 2. Sí, hay mucho código aquí, ¿verdad? Pero no se preocupe; como pronto verá, es más sencillo de lo que parece.

Figura 2 Un 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 comenzar, definimos cuatro constantes:

  • ForReading: usaremos esta constante al abrir y leer el archivo de texto.
  • adVarChar: esta es una constante ADO estándar para crear un campo que usa un tipo de datos Variant.
  • MaxCharacters: es una constante ADO estándar que se usa para indicar el número máximo de caracteres (en este caso, 255) que puede almacenar un campo Variant.
  • adDouble: una última constante ADO, para crear un campo que usa un tipo de datos doble (numérico).

Después de definir las constantes, nos encontraremos con este bloque de código:

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

Esta es la parte del script donde realmente establecemos y configuramos el conjunto de registros desconectado. Para llevar a cabo esta tarea, lo primero que hacemos es crear una instancia del objeto ADOR.Recordset; no hace falta decir que ese objeto crea la tabla virtual de la base de datos (es decir, el conjunto de registros desconectado).

A continuación usamos la siguiente línea de código (y el método Append) para agregar un nuevo campo al conjunto de registros:

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

Como se puede observar, hasta aquí no hay nada extraño: simplemente llamamos al método Append, seguido de tres parámetros:

  • El nombre del campo (Players).
  • El tipo de datos del campo (adVarChar).
  • El número máximo de caracteres que se pueden almacenar en el campo (MaxCharacters).

Después de agregar el campo Players, podemos agregar un segundo campo: HomeRuns, que tiene un tipo de datos numérico (adDouble). Al terminar esa tarea, llamamos al método Open para declarar que el conjunto de registros está abierto y listo para empezar.

A continuación, creamos una instancia de Scripting.FileSystemObject y abrimos el archivo C: \Scripts\Test.txt. En realidad, esta parte del script no tiene nada que ver con el conjunto de registros desconectado; sólo aparece allí porque necesitamos recuperar los datos de un archivo de texto. La primera línea del archivo de texto contiene la información de encabezado:

Player     Home Runs     RBI        Average

No necesitamos esta información para el conjunto de registros, así que lo primero que hacemos después de abrir el archivo es llamar al método SkipLine para saltar esta primera línea:

objFile.SkipLine

Ahora que nos situamos en la primera línea que contiene los verdaderos datos, establecemos un bucle Do Until, diseñado para que nos permita leer el resto del archivo, línea por línea. Cada vez que leemos una línea del archivo, almacenamos ese valor en una variable denominada strLine y usamos la función Split para convertir esa línea en una matriz de valores (al dividir la línea cada vez que encontramos una tabulación):

arrStats = Split(strStats, vbTab)

Es cierto, es una especie de introducción rápida, pero a estas alturas esperamos que la mayoría tenga experiencia en recuperar información de archivos de texto. Resumiendo, la primera vez que pase por el bucle, la matriz arrStats contendrá los elementos mostrados en la figura 3.

Figura 3 Contenido de la matriz

Número de elemento Nombre del elemento
0 D Pedroia
1 4
2 28
3 .276

Ahora estamos listos para divertirnos un poco:

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

Aquí agregamos al conjunto de registros desconectado la información del jugador 1 (D Pedroia). Para agregar un registro al conjunto de registros, comenzamos llamando al método AddNew; así creamos un nuevo registro en blanco, con el que podemos trabajar. Usamos las dos líneas de código siguientes para asignar valores a dos campos del conjunto de registros (Player y HomeRuns), a continuación llamamos al método Update para escribir oficialmente ese registro en el conjunto de registros. Y a continuación regresamos al comienzo del bucle, donde repetimos el proceso con la línea siguiente, el siguiente jugador, del archivo de texto. ¿Lo ve? Quizás haya demasiado código aquí, pero todo es bastante sencillo y fácil.

Entonces, ¿qué sucede después de agregar todos los jugadores al conjunto de registros? Bueno, después de cerrar el archivo de texto, ejecutamos el bloque de código siguiente:

DataList.MoveFirst

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

En la línea 1 usamos el método MoveFirst para posicionar el cursor al comienzo del conjunto de registros; de no hacerlo, corremos el riesgo de mostrar sólo algunos de los datos del conjunto de registros. A continuación establecemos un bucle Do Until que continuará hasta que no haya más datos (es decir, hasta que la propiedad EOF, fin de archivo, del conjunto de registros sea True).

En el interior del bucle, todo lo que hacemos es devolver los valores de los campos Player y HomeRuns (tenga en cuenta la sintaxis algo extraña que se usa para indicar un campo concreto: DataList.Fields.Item("Player"). Y a continuación, simplemente llamamos al método Move­Next para pasar al registro siguiente del conjunto de registros.

Ni falta hace decir que fue realmente fácil. Cuando todo haya concluido, deberíamos obtener lo siguiente:

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 verá, si lo pensamos un poco, en realidad no está del todo bien, ¿no cree? De acuerdo, obtuvimos los nombres de los jugadores y los totales de cuadrangulares, pero no obtuvimos esos totales en un orden determinado. ¡Vaya! ¿Por qué el conjunto de registros desconectado no se encargó de ordenar los datos?

En realidad hay un buen motivo: no le hemos indicado al conjunto de registros según qué campo deseamos ordenar. Pero esto es muy fácil de corregir: sólo tiene que modificar el script para agregar la información de orden justo antes de la llamar al método MoveFirst. Es decir, haga que esa parte del script sea parecida a esta:

DataList.Sort = "HomeRuns"
DataList.MoveFirst

Obviamente aquí no hay truco; simplemente asignamos el campo HomeRuns a la propiedad Sort. Ahora observe la salida que obtenemos al ejecutar el 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

Mucho mejor. Bueno, salvo por una cosa: normalmente los totales de los cuadrangulares se enumeran en orden descendente, con el jugador que posee el mayor número de cuadrangulares en primer lugar. ¿Hay alguna manera de ordenar un conjunto de registros desconectado en orden descendente?

Por supuesto que la hay; todo lo que debemos hacer es añadir el útil parámetro DESC, de este modo:

DataList.Sort = "HomeRuns DESC"

¿Y qué hace el parámetro DESC por nosotros? Exacto:

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 propósito, está permitido ordenar por múltiples propiedades; todo lo hay que hacer es asignar cada una de esas propiedades al criterio de ordenación. Por ejemplo, supongamos que desea ordenar primero por cuadrangulares y luego por carreras impulsadas. Ningún problema, este comando cumplirá con su objetivo:

DataList.Sort = "HomeRuns DESC, RBI DESC"

Inténtelo y compruébelo por sí mismo. No es tan divertido como comprobar el correo electrónico durante las vacaciones, pero se aproxima.

Nota Tenga en cuenta que no se puede ordenar por un campo que no haya sido agregado al conjunto de registros. ¿Qué significa eso? Significa que antes de agregar una propiedad como RBI a la instrucción Sort, debe agregar las siguientes líneas al script en las ubicaciones apropiadas:

DataList.Fields.Append "RBI", adDouble

DataList("RBI") = arrStats(2)

Y si desea consultar la salida, también debe modificar la instrucción Wscript.Echo:

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

Veamos, ¿qué más se puede hacer con los conjuntos de registros desconectados? Ah, por aquí hay algo. Supongamos que recuperamos toda la información de los jugadores y después ordenamos los datos por el promedio de bateo. (Entre otras cosas, eso significa que necesitamos modificar el script original para crear campos denominados RBI y Batting­Average). La salida tiene el siguiente aspecto:

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

Está bien, pero qué sucede si sólo deseamos una lista de los jugadores que golpean con .300 como mínimo? ¿Cómo podemos limitar los datos mostrados a los jugadores que coinciden con algún criterio especificado? Bueno, una solución es asignar un filtro al conjunto de registros:

DataList.Filter = "BattingAverage >= .300"

La finalidad general del filtro de un conjunto de registros es la misma que la de una consulta de base de datos: Ofrece un mecanismo para limitar los datos devueltos a un subconjunto de todos los registros del conjunto de registros. En este caso simplemente solicitamos que el Filtro elimine todos los registros menos aquellos en que el campo Batting­Average tenga un valor mayor o igual que .300. Y ¿sabe qué? El filtro hará exactamente lo que le solicitamos:

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

¿Qué le parecería que nuestros niños respondieran así?

A propósito, se puede usar múltiples criterios en un solo filtro. Por ejemplo, este comando limita los datos devueltos a los registros en los que el campo BattingAverage es mayor o igual que .300 y el campo HomeRuns es mayor que 10:

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

Por el contrario, este filtro limita los datos devueltos a los registros en los que el campo BattingAverage es mayor o igual que .300 o el campo HomeRuns es mayor que 10:

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

Pruebe los dos y así verá la diferencia. Pero, ¡qué demonios!: sólo por diversión, aquí hay otro filtro que puede probar:

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

Según parece, también puede usar caracteres comodines en los filtros. Para ello, use el operador LIKE (en contraposición con el signo igual) y después use el asterisco del mismo modo que lo hace al ejecutar un comando de MS-DOS® como dir C:\Scripts\*.txt. En el ejemplo anterior, deberíamos obtener una lista de jugadores cuyos nombres comienzan con la letra I; esto se debe a que la sintaxis que empleamos dice, "muestre una lista de todos los registros donde el valor del campo del jugador comience con una I y luego vaya seguida por, bueno, por cualquier cosa". Pruébela, aunque, vale, a estas alturas ya conoce la rutina.

Por cierto, tampoco está atascado con promedios de bateo como 0.309. (Normalmente los promedios de bateo se expresan sin el 0 inicial, por ejemplo .309). Pero está bien; simplemente puede usar la función FormatNumber para dar formato al promedio de bateo de cualquier forma antigua que desee:

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

Sólo tiene que incluir esta función en la instrucción Wscript.Echo al mostrar el número (u opcionalmente, podría asignar la salida a una variable y poner esa variable en la instrucción 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)

El tema es divertido, ¿eh?

Aunque, desafortunadamente, parece que se ha sido todo por este mes. Recapitulando, sólo nos gustaría decir que..., perdón, está sonando el teléfono.

De todos modos, queríamos comentar que... Genial, ahora está sonando el teléfono móvil. Y acabamos de recibir un correo electrónico del tostador. Importante: nuestras tostadas bien calientes están listas, ¿queremos mantequilla o mermelada? Tenemos que irnos, pero lo veremos el mes que viene.

Dr. Scripto's Scripting Perplexer

El desafío mensual que pone a prueba no sólo sus habilidades para resolver rompecabezas, también sus habilidades para el scripting.

Septiembre de 2008: Búsqueda de Scripting

He aquí una simple (o quizás no tan simple) búsqueda de palabras. Encuentre todas las funciones e instrucciones de VBScript de la lista. Pero incluye una novedad: con las letras restantes se obtendrá una palabra oculta, que resulta ser, exactamente, un cmdlet de Windows PowerShell™.

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

RESPUESTA:

Dr. Scripto's Scripting Perplexer

Respuesta: Septiembre de 2008: Búsqueda de scripting

fig08.gif

Los chicos de la sección Scripting Guys trabajan para Microsoft, mejor dicho, están contratados por Microsoft. Cuando no juegan al béisbol, ni entrenan ni lo ven (u otras actividades varias), dirigen el TechNet Script Center. Visite la página web www.scriptingguys.com.