¡Hola, chicos del scripting!

Bienvenido a la columna TechNet en la cual, por medio de Microsoft Scripting Guy, se abordan las preguntas frecuentes acerca de las secuencias de comandos para la administración de sistemas. ¿Tiene alguna pregunta sobre las secuencias de comandos para la administración de sistemas? Envíe un correo electrónico a scripter@microsoft.com. No podemos garantizarle que seremos capaces de responder todas las preguntas que nos lleguen, pero haremos todo lo posible.

Y no se olvide de consultar el Archivo de ¡Hola, chicos del scripting!.

Pregunta del día: ¿cómo puedo contar el número de palabras que tiene un archivo de texto?


¿Cómo puedo contar el número de palabras que tiene un archivo de texto?

P

¡Hola, chicos del scripting! ¿Cómo puedo contar el número de palabras que tiene un archivo de texto?

-- LA

R

Hola LA. La suya es una de esas preguntas con las que los chicos del scripting nos pasamos de listos. (Y no es que pasarnos de listos sea algo demasiado complicado, la verdad.) En primer lugar, escribimos esta columna un viernes y los viernes siempre intentamos solucionar las cosas de la forma más fácil posible. En segundo, estábamos inmersos en una discusión sobre recuentos de palabras aquel día, por lo que ya teníamos el tema en mente. Esta pregunta parecía fácil de responder y ya habíamos estado pensando en recuentos de palabras: sume esos dos factores y obtendrá la columna perfecta para un viernes.

O al menos eso pensamos.

La primera pista de problemas surgió de buenas a primeras, cuando nos sentamos a buscar la respuesta a su pregunta: existían varias formas distintas de enfocar el problema. Por ejemplo, resulta sencillo calcular recuentos de palabras (en inglés) con Microsoft Word, así que nuestro primer pensamiento fue: “Usemos Microsoft Word”. Pero eso parecía un exceso y no queríamos que se pensara que no se podían contar las palabras de un archivo de texto a menos que se abriera en Microsoft Office. (Aunque si el equipo de Office nos ofreciera algún tipo de comisión, nos replantearíamos esa posición.) Después pensamos que probablemente sería el escenario idóneo para utilizar expresiones regulares (en inglés). Pero sólo pensar en las expresiones regulares nos dio dolor de cabeza, así que también desechamos esa idea.

Finalmente dimos con esta sencilla y elegante solución:

Const ForReading = 1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)

strText = objFile.ReadAll
objFile.Close

arrWords = Split(strText, " ")
Wscript.Echo Ubound(arrWords) + 1

Francamente, muy sencilla y elegante: lo que hicimos fue abrir el archivo de texto C:\Scripts\Test.txt y almacenar el archivo de texto completo en una variable llamada strText. Posteriormente utilizamos la función Split para dividir la matriz en espacios en blanco (imaginando que sólo existirían espacios en blanco entre palabras.) Tras usar Split para crear una matriz llamada arrWords (en la que cada elemento representa una sola palabra), todo lo que debíamos hacer era devolver un eco del valor Ubound (límite superior) de la matriz, más 1. (¿Por qué más 1? Porque el valor Ubound de una matriz siempre es el número de elementos que contenga menos 1.)

Esto funcionaba, casi. Resultó que el archivo de texto que utilizamos tenía espacios en blanco adicionales para alinear la información:

Name                                        Date
Ken Myer                                    3/30/2006
Pilar Ackerman                              3/31/2006

Eso supuso un problema: cada uno de esos espacios en blanco se contaba como palabra. Por tanto, el recuento de palabras final era algo (bastante) superior al esperado.

Vuelta a empezar:

Const ForReading = 1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)

strText = objFile.ReadAll
objFile.Close

arrWords = Split(strText, " ")

For Each strWord in arrWords
    If Len(strWord) > 0 Then
        i = i + 1
    End If
Next

Wscript.Echo i

Como puede comprobar, en esta ocasión no devolvimos un eco del valor Ubound. En su lugar, configuramos un bucle For Each para que recorriera todos los elementos de la matriz. Dentro de dicho bucle empleamos la función Len para determinar el número de caracteres de cada elemento individual. Si su longitud era 0 significaba que habíamos encontrado uno de esos espacios en blanco sobrantes. En ese caso simplemente saltamos ese elemento (ya que muy pocas palabras contienen 0 caracteres). Si la longitud era superior a 0, incrementamos una variable de contador en 1:

i = i + 1

Tras recorrer con un bucle la matriz completa devolvimos un eco del valor de la variable de contador:

Wscript.Echo i

Así estaba mucho mejor, pero el recuento de palabras aún nos parecía algo elevado. Tras analizarlo un par de minutos nos dimos cuenta por qué. Imagine que el archivo de texto lo formara esta frase:

Two plus two = four

La mayoría diría que hay cuatro palabras en ella; sin embargo, nuestra secuencia insistía en que había cinco:

Two
plus
two
=
four.

¿Por qué cinco palabras? Porque la secuencia de comandos contaba el signo igual (=) como palabra. De forma similar, había otros caracteres “superfluos” en el documento: por ejemplo, esta construcción se contaba como tres palabras:

. . .

Vaya.

Como no nos gustó en absoluto, modificamos la secuencia una última vez utilizando en esta ocasión una serie de funciones Replace para reemplazar caracteres como el signo igual y el punto por espacios en blanco:

Const ForReading = 1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)

strText = objFile.ReadAll

strText = Replace(strText, ",", " ")
strText = Replace(strText, ".", " ")
strText = Replace(strText, "!", " ")
strText = Replace(strText, "?", " ")
strText = Replace(strText, ">", " ")
strText = Replace(strText, "<", " ")
strText = Replace(strText, "&", " ")
strText = Replace(strText, "*", " ")
strText = Replace(strText, "=", " ")

strText = Replace(strText, vbCrLf, " ")

objFile.Close

arrWords = Split(strText, " ")

For Each strWord in arrWords
    If Len(strWord) > 0 Then
        i = i + 1
    End If
Next

Wscript.Echo i

El resultado fue más de nuestro agrado. Como en las secuencias de comandos anteriores comenzamos definiendo una constante llamada ForReading, que indica a FileSystemObject que deseamos leer el archivo de texto (en lugar de escribir o agregar). A continuación, creamos una instancia de FileSystemObject y utilizamos el método OpenTextFile para abrir el archivo C:\Scripts\Test.txt. Una vez que obtenemos FileSystemObject y logramos que se ejecute, empleamos el método ReadAll para leer todo el archivo en una variable denominada strText:

strText = objFile.ReadAll

Posteriormente ejecutamos una serie de funciones Replace para reemplazar los caracteres en la variable strText. (Observe que no modificamos el archivo real, sino una copia del mismo almacenada en memoria.) Por ejemplo, esta línea de código reemplaza todas las comas en strText por un espacio en blanco:

strText = Replace(strText, ",", " ")

Dejaremos en sus manos la decisión de qué caracteres desea reemplazar, si es que fuera necesario. Si le conviene contar los signos igual y más (+) como palabras individuales entonces quizá no deba hacer ningún reemplazo.

Espere, observe esto: sí que hay un reemplazo que quizá deba hacer. Suponga que tenemos un archivo de texto con un aspecto similar al siguiente:

A
B
C
D
E

¿Cuantas palabras contiene este archivo? También diríamos que cinco, pero la secuencia indica que sólo una. ¿Por qué motivo? Bien, indicamos a la secuencia que debía dividir el texto al llegar a un espacio en blanco, pero este archivo no tiene ningún espacio en blanco, sólo retornos de carro o saltos de línea al final de cada línea. Por esta razón la matriz sólo contiene un elemento. Vaya.

Así que, ¿cómo podemos solucionar ese problema? La verdad es que es bastante fácil: simplemente reemplazamos todos los retornos de carro o saltos de línea (vbCrLf) por espacios en blanco:

strText = Replace(strText, vbCrLf, " ")

Una vez que tenemos espacios entre cada carácter (en lugar de retornos de carro o saltos de línea), la secuencia de comandos informará correctamente de la existencia de cinco palabras en el archivo de texto de ejemplo.

Bueno, ¿en qué estábamos? Ah, cierto. Tras cerrar el archivo, llamamos a la función Split para dividir strText en una matriz. Luego empleamos el bucle For Each ya mostrado anteriormente para contar el número de palabras de la matriz (y así el número de palabras del archivo de texto), omitiendo todos los espacios en blanco. A continuación, devolvemos un eco del valor de la variable de contador y con eso concluimos.

Al menos para satisfacción nuestra. Que el recuento de palabras sea preciso o no al 100% es algo subjetivo. Por ejemplo, supongamos que tenemos la siguiente línea en el archivo de texto:

2+2=4
¿Hay cinco palabras en esta línea (2, +, 2, = y 4)? Quizá sólo haya tres: 2, 2 y 4. O quizá sólo haya una: 2+2=4. (Microsoft Word considera que esto es una sola palabra.) Tendrá que tomar esa decisión por sí mismo. En cuanto a nosotros, hemos decidido que la próxima vez que encontremos una pregunta “fácil”, ¡la dejaremos a un lado y nos ocuparemos de alguna otra!

Para más Información

Consulte el Archivo de ¡Hola, chicos del scripting!.

Arriba Arriba