¡Hola, encargados del scripting!Las expresiones regulares saltan a la vista

Los chicos del scripting de Microsoft

Descargar el código de este artículo: HeyScriptingGuy2008_01.exe (150KB)

Todos cometemos errores.(¿Acaso cree que el equipo de chicos del scripting fue creado a propósito?)Sin embargo, si hay algo positivo que se puede decir acerca de los errores, es... ¿Qué?¿Pensaba que íbamos a decir que lo positivo de los errores es que siempre se puede aprender de los mismos?No, por supuesto que no:lo único que puede aprender de un error es que hubiera sido mucho mejor no haberlo cometido en primer lugar.

Nota:por favor, no más chistes sobre Microsoft Bob, hoy no.Cometimos el error de burlarnos hace años de Microsoft Bob y, en ese caso, aprendimos verdaderamente de nuestro error.En lo que se refiere a Microsoft Bob, nuestros labios están sellados.

Para ser honestos, los Chicos del scripting no podemos sacar nada positivo de los errores; por eso hemos decidido dedicar nuestras vidas a minimizar los errores que cometemos.En realidad, la única manera en la que podríamos minimizar nuestros errores consistiría en minimizar el número de cosas que hacemos realmente. Pero esa es otra historia.

Ahora, está muy bien que hayamos tratado de minimizar nuestros propios errores; sin embargo, eso no será de gran ayuda si las personas con las que trabajamos continúan cometiendo errores.(No, por favor... ya se lo hemos dicho:¡Ningún chiste sobre Microsoft Bob!)Si ha escrito alguna vez un front-end para, digamos, un programa de base de datos o para Active Directory® (sabemos que muchos lo han hecho), entonces sabe exactamente de lo que hablamos:su programa de entrada de datos front-end es sólo tan bueno como las personas que llevan a cabo la entrada de datos.

Suponga que desea que los nombres de pila se escriban de esta forma:Ken, la primera con mayúscula y las siguientes letras en minúscula.¿Qué sucede cuando un usuario escribe ese nombre de esta manera:KEN?Suponga que las fechas se tienen que escribir así:01/23/2007. ¿Qué sucede cuando un usuario escribe una fecha de esta manera:Enero 23, 2007?Suponga... bueno, ya capta la idea.Como hemos dicho, su programa de entrada de datos front-end es tan bueno sólo como las personas que llevan a cabo la entrada de datos.Y, le guste o no, las personas encargadas de la entrada de datos van a cometer errores.A menos que, por supuesto, siga los pasos para asegurarse de que no los cometan.

Asegúrese de que sus datos sean válidos

Abandonemos los juegos de una vez

¿Se ha preguntado alguna vez qué evento ansían los chicos del scripting durante todo el año?Si ha asistido al Script Center en febrero durante alguno de los dos últimos años, podría pensar que ese evento es "Los Juegos de invierno de Scripting".Bien, pues se equivocaría.Los chicos del scripting ansían la finalización de los Juegos de Scripting: tras dos semanas de diversión y emoción sin pausa, estos disfrutan del calor de la victoria obtenida en los Juegos de Scripting y se dedican a dormir durante el mes siguiente.

Así que, en preparación para nuestro evento del año favorito, el final de los Juegos de Scripting, ha llegado el momento de comenzar "Los Juegos de invierno de Scripting 2008".Únase a nosotros del 15 de febrero al 3 de marzo de 2008 en el Script Center para disfrutar de más de dos semanas de diversión y la mejor competición de scripting existente.

Los Juegos de Scripting son una manera excelente de comprobar y mejorar sus habilidades de scripting.Y, ya que los chicos del scripting quieren dormir unos cuantos días más una vez que la competición finalice, ¡vamos a hacer de este evento algo aún más grande y mejor que el año pasado!Una vez más, tendremos dos divisiones:Principiante y Avanzado.Esto significa que ya sea usted un recién llegado al mundo del scripting, un profesional veterano (o joven), o alguien a medio camino entre ambas cosas, esta competición es para usted.

Así que ¿qué hay de nuevo este año?Vamos a agregar otro lenguaje de scripting.Puede entrar en la competición en VBScript, Windows PowerShell o, en la novedad, Perl.Sabemos que Perl no es nuevo, pero sí lo es en esta competición.Seguramente encontraremos más diversión pero, como tuvimos que completar este artículo hace varios meses, no tenemos la menor idea de lo que podrá ser.Tendrá que venir al Script Center para conocer todas las novedades.

En caso de que se lo haya preguntado... sí, ¡habrá premios!¿Cuáles serán?Un momento, no vamos a estropear la sorpresa, así que venga al Script Center y descúbralo. microsoft.com/technet/scriptcenter/funzone/games/default.mspx.

Puede que muchos de ustedes ya realicen una validación rudimentaria de la entrada de datos.Aunque, para muchas cosas, esta validación rudimentaria de la entrada de datos sea perfectamente adecuada.¿Necesita asegurarse de que el valor de cadena strName tiene 20 o menos caracteres?Este diminuto bloque de código lo hará:

If Len(strName) > 20 Then
    Wscript.Echo "Invalid name; a " & _
    "name must have 20 or fewer characters."
End If

Pero suponga que strName es un número de pieza nuevo, y los números de pieza deben cumplir un patrón específico:cuatro dígitos seguidos por dos letras mayúsculas (por ejemplo, 1234AB).Mediante un VBScript convencional, ¿es posible comprobar que ese strName sigue el patrón necesario para números de pieza?Sí, es posible.Pero créanos, no querrá hacerlo.En su lugar, use una expresión regular.

Asegúrese de que sólo aparecen números en un valor

Las expresiones regulares se remontan a los años cincuenta, cuando fueron descritas por primera vez como una forma de notación matemática.En los años sesenta, este modelo matemático se incluyó en el editor de texto QED como una manera de buscar patrones de carácter en un archivo de texto.Posteriormente... ¿Qué?Vaya.Parece que no todo el mundo encuentra fascinante la historia de las expresiones regulares. De acuerdo, muy bien.En ese caso, simplemente le mostraremos de lo que estamos hablando.

¿Desea asegurarse de que una variable (en este caso, la variable strSearchString) contiene sólo números?La Figura 1 muestra una forma de hacerlo.

Figure 1 Sólo números, por favor

Set objRegEx = _
  CreateObject("VBScript.RegExp")

objRegEx.Global = True   
objRegEx.Pattern = "\D"

strSearchString = "353627"

Set colMatches = _
  objRegEx.Execute(strSearchString)  

If colMatches.Count > 0 Then
    Wscript.Echo "Invalid: a " & _
      "non-numeric character was found."
Else
    Wscript.Echo "This is a valid entry."
End If

Como puede ver, el script comienza creando una instancia del objeto VBScript.RegExp, el objeto que... sí, tiene razón... el objeto que nos permite usar las expresiones regulares en nuestro script.Ah, queríamos encargarnos de explicarlo.Después, asignamos valores a dos propiedades clave del objeto de expresiones regulares:Global y Pattern.

La propiedad Global determina si el script debe coincidir con todas las apariciones de un patrón o debe encontrar simplemente la primera de estas apariciones y, a continuación, detenerse.En general, es mejor configurar este valor en True, lo cual significa que desea buscar todas las apariciones de un patrón.El valor predeterminado es False.La mayoría de las veces, querrá encontrar todas las apariciones de un patrón.Incluso si no es así, la única ventaja de encontrar sólo la primera aparición de un patrón es que el script se ejecutará más rápido si se detiene en algún lugar de la cadena en vez de buscar completamente hasta el fin.En teoría, eso suena bastante bien.En la práctica, no obstante, la búsqueda se completa normalmente de una forma tan rápida que no notará diferencia.

Esta es la parte positiva:el patrón es el lugar donde especificamos los caracteres (y la serie de caracteres) en que estamos interesados. En nuestro script de ejemplo, buscamos cualquier carácter que no sea uno de los dígitos del 0 al 9. Si encontramos un carácter así (una letra, un signo de puntuación, o lo que sea), sabremos entonces que el valor asignado a strSearchString no es válido.Con expresiones regulares, la sintaxis \D se usa para hacer coincidir cualquier carácter que no sea un dígito; por lo tanto, asignamos el valor \D a la propiedad Pattern.

Nota:¿Cómo supimos que \D coincidía con cualquier carácter que no sea un dígito?Bien, hay una larga historia detrás.Verá, hace varios años... Bueno, vale.Lo miramos en la referencia del lenguaje VBScript en MSDN® en msdn2.microsoft.com/1400241x.

Tras asignar valores a las propiedades Global y Pattern, el siguiente paso es asignar un valor a la variable strSearchString:

strSearchString = "353627"

Así pues, ¿cómo sabemos si hay algún carácter que no sea un dígito en esta cadena?Muy fácil.Llamamos al método Execute, que busca en nuestra variable cualquier carácter que no sea un dígito:

Set colMatches = _
objRegEx.Execute(strSearchString)

Cuando llama al método Execute, cualquier coincidencia, es decir, cualquier instancia de Pattern, que se encuentre se almacena automáticamente en la colección Matches.En nuestro script de ejemplo, dimos a esta colección el nombre colMatches.Si queremos saber si nuestra variable contiene o no cualquier carácter que no sea un dígito (y queremos saberlo), todo lo que tenemos que hacer es comprobar el valor de la propiedad Count de la colección, que nos dice cuántos elementos hay en la colección:

If colMatches.Count > 0 Then
    Wscript.Echo "Invalid: a " & _
      "non-numeric character was found."
Else
    Wscript.Echo "This is a valid entry."
End If

Si la propiedad Count es mayor que 0, puede significar sólo una cosa:que se ha encontrado un carácter que no es un dígito en algún lugar del valor de la variable.Si todos los caracteres del valor fueran dígitos, la colección estaría vacía y, entonces, tendría una propiedad Count de 0.En nuestro script de ejemplo, avisábamos del descubrimiento de un carácter no numérico.En un front-end real de script o de base de datos, devolveríamos un mensaje similar y volveríamos atrás para que el usuario lo intentara otra vez.Será usted el que deba preocuparse de eso, pues nosotros, como chicos del scripting, tenemos muchas otras cosas de las que preocuparnos.

¿Cómo qué?Como que, por ejemplo, algún día los suscriptores de TechNet Magazine comiencen a leer realmente los artículos que les enviamos cada mes.Preferimos que no aprendan de sus errores.

Buena pregunta:¿No podríamos usar simplemente la función IsNumeric para determinar si strSearchString contiene o no un número?Por supuesto, siempre que strSearchString pueda ser cualquier número.Pero, ¿qué pasa si strSearchString debe ser un entero positivo?Eso podría ser un problema; IsNumeric identifica estos dos números como válidos:

-45
672.69

¿Por qué se identifican estos números como válidos?Porque son números válidos; simplemente, no son enteros positivos.Nuestra expresión regular marcará estas entradas como no válidas, sin embargo, puesto que la expresión regular captura el signo menos (-) y el punto (.) de los caracteres que no son dígitos.Si, al leer esta columna, ha pensado: "¿Por qué estoy leyendo esta columna?" Bien, hay una buena razón ahí mismo.

¿Qué ha dicho?¿No es ese un motivo lo suficientemente bueno?Vaya, tenemos un público difícil este mes.De acuerdo, echemos un vistazo a otros tipos de validación de entrada de datos que se pueden realizar usando expresiones regulares.

Asegúrese de que no aparecen números en un valor

Acabamos de mostrar cómo asegurar que sólo aparezcan números en un valor.¿Qué pasaría si quisiéramos hacer lo contrario? ¿Y si quisiéramos asegurarnos de que no aparecen números en nuestro valor?Bien, aquí hay un patrón de búsqueda que podemos usar:

objRegEx.Pattern = "[0-9]"

¿Qué ocurre aquí?Bien, en el mundo un poco alocado y salvaje de las expresiones regulares, los caracteres de corchetes ([ y ]) permiten especificar un conjunto de caracteres o, como hemos hecho aquí, un rango de caracteres.¿Qué significa el patrón [0-9]?Significa que estamos buscando cualquier carácter en el rango 0 a 9, en otras palabras, cualquier valor numérico.Si queremos sólo números pares (es decir, si deseamos asegurarnos de que nuestro valor no incluya ningún 1, 3, 5, 7 o 9), usaríamos este patrón:

objRegEx.Pattern = "[13579]"

Tenga en cuenta que, como no usamos un guión, no buscamos un rango de caracteres.De hecho, buscamos los caracteres 1, 3, 5, 7 y 9, específicamente.

Por supuesto, el patrón [0-9] busca sólo números; no encontrará los signos de puntuación ni otros caracteres que no son letras pero que no son números tampoco.¿Cree que podemos crear un patrón que busque cualquier carácter que no sea una letra?Por supuesto que podemos.Puede hacer prácticamente cualquier cosa que desee con expresiones regulares:

objRegEx.Pattern = "[^A-Z][^a-z]"

En este caso, vamos a combinar dos criterios en nuestro patrón:buscamos [^A-Z] y [^a-z].Como ya se habrá imaginado, A-Z significa el rango de caracteres desde A mayúscula a Z mayúscula. ¿Qué significa el carácter ^?Cuando va incluido entre un par de corchetes, ^ significa "buscar cualquier carácter que no se encuentre en el conjunto de caracteres".Por lo tanto, [^A-Z] significa "buscar cualquier carácter que no aparezca en mayúscula".Del mismo modo, [^a-z] significa "buscar cualquier carácter que no aparezca en minúscula".El resultado final:este patrón significa que buscamos algo que no está en letra mayúscula ni en letra minúscula.Esto incluye números, signos de puntuación y cualquier otro carácter extraño que pudiera encontrar en su teclado.

De forma alternativa, podríamos haber establecido la propiedad IgnoreCase en True:

objRegEx.IgnoreCase = True

Con esto, nuestra búsqueda omitiría si las letras se escriben en mayúscula o en minúscula.Al mismo tiempo, podríamos usar este patrón, que combinado con la propiedad IgnoreCase, buscaría cualquier carácter que no estuviera en mayúscula ni en minúscula:

objRegEx.Pattern = "[^A-Z]"

Asegúrese de que el número de pieza es válido

Vamos a adoptar un tono más elegante.Y, confíe en nosotros, es posible adoptar un tono muy elegante con expresiones regulares; visite el sitio web regexlib.com para ver algunos ejemplos.Anteriormente en este artículo, dijimos algo parecido a esto:Suponga que strName es un número de pieza nuevo, y que los números de pieza deben cumplir un patrón especificado:cuatro dígitos seguidos por dos letras mayúsculas (por ejemplo, 1234AB).¿Cómo puede comprobar que una variable cumple este patrón?Pues así, claro:

objRegEx.Pattern = "^\d{4}[A-Z]{2}"

¿Qué significa este patrón?Qué casualidad:estábamos a punto de explicar lo que significa este patrón.En este caso, estamos buscando tres criterios:

^\d{4} 

\d significa que estamos buscando dígitos (un carácter en el rango de 0 a 9).¿Qué significa {4}?Significa que deseamos la coincidencia exacta de cuatro de los caracteres (es decir, exactamente cuatro dígitos).Así que buscamos cuatro dígitos consecutivos.

¿Y el carácter ^?Bien, fuera de un par de corchetes, ^ indica que el valor debe empezar con el patrón subsiguiente; lo que significa que AA1234AB no se marcaría como una coincidencia.¿Por qué?Porque el valor empieza realmente con AA, no con cuatro dígitos consecutivos.Si quisiéramos, podríamos usar el carácter $ para indicar que la coincidencia debe producirse al final de la cadena.Pero no queremos.Bueno, al menos no en este ejemplo.

[A-Z]{2} 

Ya sabe lo que significa el componente [A-Z]; es cualquier letra mayúscula.¿Y el {2}?Exacto:los cuatro dígitos deben aparecer seguidos exactamente por dos caracteres en mayúscula.

Es bastante fácil, ¿no?A propósito, en este caso, si Count es 0, tendremos un valor no válido.¿Por qué?Esta vez no estamos buscando un solo carácter que pueda invalidar la cadena; en su lugar, buscamos una coincidencia exacta del patrón.Si no obtenemos tal coincidencia, eso significa que tenemos un valor no válido.Y también significa que la propiedad Count de colección será 0.

Nota:estas son otras dos construcciones útiles:{3,} y {3,7}.{3,} significa que debe haber al menos 3 caracteres consecutivos (o expresiones); sin embargo, mientras hay un mínimo de 3, no hay máximo.\d{3,} coincide con 123, coincide con 1234 y también con 123456789. {3,7} significa que debe haber por lo menos 3 caracteres consecutivos (o expresiones) pero no más de 7. Así, 123456 es una coincidencia, pero 123456789 (que tiene más de 7 dígitos consecutivos) no lo es.

Este es otro patrón útil.Suponga que el número de la pieza comienza con cuatro dígitos seguidos por las letras US y dos caracteres finales (que pueden ser letras, números o cualquier cosa).Esta es una forma de conseguir la coincidencia:

objRegEx.Pattern = "^\d{4}US.."

Como puede ver, este patrón comienza con cuatro dígitos (\d), que deben aparecer seguidos por los caracteres US (y sólo los caracteres US; nada más será una coincidencia).Después de los caracteres US, necesitamos dos caracteres más de cualquier clase.¿Cómo indicamos cualquier carácter en una expresión regular?Un segundo; sabemos la respuesta... Espere, ya lo tenemos:cualquier carácter único se indica con un punto (.).Lógicamente, eso significa que dos caracteres pueden ir indicados por dos puntos:

..

De acuerdo, eso era fácil.Probemos algo más difícil.Suponga que estos dos caracteres internos indican el país donde se fabricó la pieza.Si tiene plantas de producción en Estados Unidos, el Reino Unido y España, eso significa que cualquiera de estos códigos de dos caracteres es válido:US, UK, ES.¿Cómo podemos, entonces, establecer varias opciones en una expresión regular?

Bien, qué tal así:

objRegEx.Pattern = "^\d{4}(US|UK|ES).."

La clave aquí es esta pequeña construcción:(US|UK|ES).Hemos colocado los tres valores aceptables (US, UK y ES) dentro de un conjunto de paréntesis, separando cada valor con el carácter de barra vertical (|).Así es como se establecen varias opciones con una expresión regular.

Pero espere:¿No dijimos que podía colocar varias opciones entre corchetes?¿No era eso para lo que se utilizaba [13579]?Sí, lo era.Sin embargo, esto funciona sólo para caracteres sueltos; [13579] siempre se interpretará como 1, 3, 5, 7 y 9. Para usar 135 y 79 como opciones, necesitará usar esta sintaxis:(135|79). También puede usar paréntesis para delinear una sola palabra que se debe buscar:

objRegEx.Pattern = "(scripting)"

O simplemente omita los paréntesis:

objRegEx.Pattern = "scripting"

Nota:de acuerdo, pero, ¿qué pasa si se necesita incluir los paréntesis como parte del término de búsqueda?, es decir, ¿qué pasa si quiero buscar (scripting)?Bien, ya que tanto los paréntesis abiertos como los cerrados son caracteres reservados en expresiones regulares, necesita colocar \ antes de cada carácter.En otras palabras:

objRegEx.Pattern = "\(scripting\)"

Libertad de expresión

Esto es todo para lo que hemos tenido tiempo este mes.Si desea obtener más información acerca de expresiones regulares, bueno, quizás desee echar un vistazo al webcast que trata la teoría de cadenas para administradores de sistemas:introducción a las expresiones regulares (microsoft.com/technet/scriptcenter/webcasts/archive.mspx).Y, a medida que empiece a aprender y a usar expresiones regulares, tenga presentes las palabras inmortales de Sophia Loren:"Los errores forman parte de los derechos que uno paga por una vida completa".

¡Anda!¡Los chicos del scripting hemos vivido unas vidas mucho más completas de lo que creíamos!

Dr. Scripto's Scripting Perplexer

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

Enero de 2008:Nombres escurridizos

Este mes Dr. Scripto decidió trabajar con Windows PowerShell.Por supuesto, no se puede trabajar con Windows PowerShell, al menos, no si lo utiliza correctamente (y Dr. Scripto siempre lo hace todo correctamente), sin usar cmdlets.Para este rompecabezas, Dr. Scripto ha repartido algunos cmdlets por una cuadrícula y debe encontrarlos.

Eso si, tenemos que admitir que este rompecabezas sería un poco más confuso si hubiéramos incluido el cmdlet completo.A causa de la construcción verbo-nombre de los cmdlets, hay docenas que comienzan con Get-, Set-, etc.Así que abandonamos la parte verbal del cmdlet (y el guión), dejando sólo los nombres.Pero podemos decirle que todos los verbos son "Get-".

Su tarea este mes consiste en descubrir los nombres de cmdlet.Le proporcionamos la lista de nombres oculta en el rompecabezas.¿Somos generosos, verdad?Cada palabra puede ir en vertical y en horizontal, hacia delante y hacia atrás, pero nunca en diagonal.Le damos la primera palabra (Location) como ejemplo.Buena suerte.

Lista de palabras

Alias
ChildItem
Credential
Date
EventLog
ExecutionPolicy
Location
Member
PSSnapin
TraceSource
Unique
Variable
WMIObject

ANSWER:

Dr. Scripto's Scripting Perplexer

Respuesta:Nombres escurridizos, enero de 2008

Los chicos del scripting de MicrosoftLos chicos del scripting trabajan para, bueno, son empleados de 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.

© 2008 Microsoft Corporation and CMP Media, LLC. Reservados todos los derechos; queda prohibida la reproducción parcial o total sin previa autorización.