Skip to content
Lesson 9 of 9

Step 1 of 5 · Reading · ~2 min

Learn

Strings, Regex, I/O

Groovy makes file I/O concise. Most of the API below is Groovy JDK sugar layered on java.io.File.

Reading a file

def file = new File("data.txt")
file.text                    // entire content as String
file.bytes                   // entire content as byte[]
file.readLines()             // List<String>
file.eachLine { line, num ->
    println "$num: $line"
}

eachLine hands the closure the line and a 1-based line number.

Writing

new File("out.txt").text = "hello" + '\n'   // REPLACES the file
new File("log.txt") << "appended"            // appends

Assigning .text truncates first; << on a File appends. Reaching for the wrong one silently destroys a log.

Stream-style for big files

new File("big.csv").withReader { reader ->
    def line
    while ((line = reader.readLine()) != null) {
        process(line)
    }
}

withReader closes the reader when the block exits, however it exits — that is the whole point of the with* family, and it is why you rarely write a try/finally around a file in Groovy. withWriter, withInputStream and withOutputStream behave the same way.

Walking a directory

new File("src").eachFile { f -> println f.name }        // children
new File("src").eachFileRecurse { f ->                  // and below
    if (f.name.endsWith(".groovy")) println f.absolutePath
}

stdin and stdout

This is the part the grader actually exercises, because a submission has no filesystem to speak of — it has a pipe on stdin.

System.in.eachLine { line, num -> println "$num: $line" }
def all  = System.in.text                        // everything at once
def one  = System.in.newReader().readLine()      // a single line
printf("%-10s %d" + '\n', "hi", 42)              // C-style

System.in.eachLine takes the same one- or two-argument closure as the File version, so the numbering comes for free.

Asking about a file

def f = new File("data.txt")
f.exists(); f.size(); f.canRead(); f.isDirectory(); f.lastModified()

Running a process

def proc = "git status".execute()
proc.waitForOrKill(5000)
println proc.text          // stdout
println proc.exitValue()   // 0 or non-zero

// piping needs two PROCESSES, not two strings:
def out = ("ls -la".execute() | "grep groovy".execute()).text

String.execute() is what turns the command into a Process; | is defined on Process, not on String.

Your exercise

Number the lines arriving on stdin. The mistake the grader catches is starting the count at 0 — eachLine's counter is 1-based, so trust it rather than keeping your own.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…