Lectura

La clase File provee un conjunto de métodos para la entrada/salida.

Groovy provides a number of helper methods for working with I/O. All of these work with standard Java Reader3.1/Writer3.2and InputStream3.3/OutputStream3.4and File and URL classes.

Lectura de un fichero

generaciondecodigos@nereida:~/src/groovy/files$ cat -n readfile.groovy
     1  #!/usr/bin/env groovy
     2  /**
     3  Reading a text file
     4  */
     5  file = args[0]
     6  new File(file).eachLine{ line ->
     7    println line
     8  }
If for whatever reason the println() method were to throw an exception, the eachLine() method ensures that the file resource is correctly closed. Similarly if an exception occurs while reading, the resource will be closed too.

Ejecución:

generaciondecodigos@nereida:~/src/groovy/files$ ./readfile.groovy readfile.groovy
#!/usr/bin/env groovy
/**
Reading a text file
*/
file = args[0]
new File(file).eachLine{ line ->
  println line
}

Lectura de un Fichero en una sola Operación

El acceso new File(fileName).text se traduce en una llamada al método getText()

public String getText()
El cual lee los contenidos del File y retorna la String.

generaciondecodigos@nereida:~/src/groovy/io$ cat -n slurpFile.groovy
     1  #!/usr/bin/env groovy
     2  // retrieve the content of the file
     3  if (!args) return
     4  fileName = args[0]
     5  content = new File(fileName).text
     6  println content

Existe otra forma de llamar a getText:

  String  getText(String)
que lee los contenidos del File usando la codificación especificada como argumento. En ese caso no es posible acceder via la sintáxis de atributo (.text):

generaciondecodigos@nereida:~/src/groovy/io$ cat -n slurpFile2.groovy
 1  #!/usr/bin/env groovy
 2  // you can specify the encoding of the file
 3  // note that since getText() has a parameter,
 4  // you cannot call it with something like text("UTF-8")
 5  if (!args) return
 6  fileName = args[0]
 7  content = new File(fileName).getText("UTF-8")
 8
 9  println content

Lectura desde STDIN

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

Each invocation of one of an InputStreamReader's read() methods may cause one or more bytes to be read from the underlying byte-input stream. To enable the efficient conversion of bytes to characters, more bytes may be read ahead from the underlying stream than are necessary to satisfy the current read operation.

For top efficiency, consider wrapping an InputStreamReader within a BufferedReader.

~/Lgroovy/files$ cat -n stdin.groovy 
     1  #!/usr/bin/env groovy
     2  prompt = '\n> '
     3  print 'Enter text including a digit:' + prompt
     4  new BufferedReader(new InputStreamReader(System.in)).eachLine{ line ->
     5                                                 // line is read from System.in
     6      if (line =~ '\\d') println "Read: $line"   // normal output to System.out
     7      else System.err.println 'No digit found.'  // this message to System.err
     8  }

El atributo in de la clase System se refiere al flujo de entrada estandard. Es un objeto de la clase java.io.BufferedInputStream. Del mismo modo existen los atributos out y err para referirse a la salida estandard y al flujo e errores.

The eachLine method iterates through the stream, passing each line to the given closure.

Ejecución:

~/Lgroovy/files$ ./stdin.groovy 
Enter text including a digit:
> a3b
Read: a3b
hjk
No digit found.
42
Read: 42
^D

En realidad System.in pertenece a la clase java.io.BufferedInputStream y por tanto es ya un flujo dotado de un buffer. El anterior programa puede reescribirse:

~/Lgroovy/files$ cat -n readline.groovy 
     1  #!/usr/bin/env groovy
     2  prompt = '\n> '
     3  print 'Enter text including a digit:' + prompt
     4  System.in.eachLine{ line ->
     5                                                 // line is read from System.in
     6      if (line =~ '\\d') println "Read: $line"   // normal output to System.out
     7      else System.err.println 'No digit found.'  // this message to System.err
     8  }

Matching y Verdad

El operador a =~ 'regexp' comprueba si la cadena a casa/matches con regexp.

groovy:000> line = "hello world"
===> hello world
groovy:000>  m = line =~ '[aeiou]' 
===> java.util.regex.Matcher[pattern=[aeiou] region=0,11 lastmatch=]
groovy:000> m[0]
===> e
groovy:000> m[1]
===> o
groovy:000> m.size()
===> 3

en un contexto booleano es evaluado como cierto si la cadena a pertenece al lenguaje descrito por la regexp.

The following table summarizes the rules and the order in which they are applied to evaluate truth in a condition:

Type Evaluation criteria
Boolean Value is equal to true
Matcher There is at least one match
Collection The collection is not empty
Map The map is not empty
String, GString The string is not empty
Number, Character The value is not 0 (zero)
others Non null reference

Remember that the type is determined in runtime, not at compile time.

Lectura y Filtrado de un Fichero

El siguiente programa muestra la primera ocurrencia de un enlace href en cada línea de un fichero HTML:

~/Lgroovy/files$ cat -n links.groovy 
     1  #!/usr/bin/env groovy
     2  if (args.length == 0 ) return
     3  testfile = new File(args[0])
     4  testfile.eachLine { line ->
     5     line.find(/(?ix) href \s* = \s* "( [^"]* )"/) { 
     6        delta, link -> println "delta = '$delta' link='$link'" 
     7     } 
     8  }

The String method public String find(Pattern pattern, Closure closure) finds the first occurrence of a compiled regular expression Pattern within a String.

If the pattern doesn't match, the closure will not be called and find will return null.

If it does match and we don't have any capture groups in our regex, there is a single parameter on the closure that the match gets passed to.

If we have capture groups in our expression, our closure has one parameter for the match, followed by one for each of the capture groups.

The funtion returns a String containing the result of the closure, or null if the regex pattern doesn't match

Cuando lo ejecutamos obtenemos una salida similar a esta:

~/Lgroovy/files$ ./links.groovy node17.html | head -4
delta = 'HREF  =   "mystyle.css"' link='mystyle.css'
delta = 'HREF="node18.html"' link='node18.html'
delta = 'HREF="node16.html"' link='node16.html'
delta = 'HREF="node6.html"' link='node6.html'

Lectura de un Fichero en una Lista

El método readLines nos permite leer un fichero completo y guardarlo en una lista:

groovy:000> f = new File("input.txt")
===> input.txt
groovy:000> lines = f.readLines()
===> [an, input, file]
groovy:000> lines.class
===> class java.util.ArrayList
Usando readLines podemos contar las líneas en un fichero:
groovy:000> new File("input.txt").readLines().size()
===> 3

Una vez que los contenidos del fichero se tienen en forma de lista es posible aplicar cualquiera de los métodos de manipulación de listas.

groovy:000> new File('input.txt').readLines().reverse()
===> [file, input, an]
groovy:000> new File('input.txt').readLines().sort()
===> [an, file, input]

Analizando un Fichero de Texto

En el siguiente ejemplo contaremos el número de párrafos, líneas y apariciones de la palabra captain en un fichero de texto:

groovy:000> f = new File("texto")               
===> texto                                      
groovy:000> f.text                              
===> Captain my Captain! our fearful trip is done;
The ship has weathered every rack, the prize we sought is won;
The port is near, the bells I hear, the people all exulting,  
While follow eyes the steady keel, the vessel grim and daring:

    But O heart! heart! heart!
    O the bleeding drops of red,
    Where on the deck my Captain lies,
    Fallen cold and dead.

O Captain! my Captain! rise up and hear the bells;
Rise up—for you the flag is flung—for you the bugle trills;
For you bouquets and ribbon’d wreaths—for you the shores a-crowding;
For you they call, the swaying mass, their eager faces turning;

    Here Captain! dear father!
    This arm beneath your head;
    It is some dream that on the deck,

        You’ve fallen cold and dead.

My Captain does not answer, his lips are pale and still;
My father does not feel my arm, he has no pulse nor will;
The ship is anchored safe and sound, its voyage closed and done;
From fearful trip, the victor ship, comes in with object won;

    Exult, O shores, and ring, O bells!
    But I, with mournful tread,
    Walk the deck my Captain lies,

        Fallen cold and dead.
Un párrafo se define como texto delimitado entre líneas en blanco:
groovy:000> m = f.text.findAll('\\n\\n+').size()
===> 8
El número de líneas es el número de retornos de carro:
groovy:000> m = f.text.findAll('\\n').size()
===> 35
Contemos el número de veces que aparece la palabra Captain:
m = f.text.findAll('(?i)\\bCaptain\\b').size()
===> 8



Subsecciones
Casiano Rodríguez León
2010-04-30