Groovy adds methods to java.io.File that turn common file tasks into one-liners. Perfect for scripts that prepare test data, process exports or inspect integration files.
Read and write whole files
def csv = new File('orders.csv')
csv.text = 'id,customer,total\nPO-1,Acme,250\n' // write (replaces the file)
csv << 'PO-2,Globex,90\n' // append
println csv.text // read everything
def lines = csv.readLines() // a List<String>
Large files: line by line
text and readLines() load the whole file into memory. For big files, stream them:
csv.eachLine { line, number ->
if (number == 1) return // skip the header
def (id, customer, amount) = line.split(',')
// process one line at a time
}
CSV rows as maps
def header = lines.head().split(',')
def rows = lines.tail().collect { l -> [header, l.split(',')].transpose().collectEntries() }
rows.findAll { it.customer == 'Acme' }*.id
transpose() pairs each header with its value, and collectEntries() turns the pairs into a map — so each row becomes [id:PO-1, customer:Acme, total:250].
Watch out: Splitting on commas breaks on quoted values like "Acme, Inc.". For real-world CSV, use a proper parser (for example Apache Commons CSV, or groovy-csv).
Writers and readers that close themselves
new File('report.txt').withWriter('UTF-8') { w ->
w.writeLine('Acme: 2 order(s)')
}
new File('orders.csv').withReader('UTF-8') { r -> println r.readLine() }
Like Java’s try-with-resources, withWriter and withReader close the file even if an exception is thrown.
Directories
def dir = File.createTempDir()
dir.eachFile { println it.name }
dir.eachFileRecurse { println it.path }
dir.deleteDir()
The full example
// files.groovy — reading and writing files the Groovy way.
def dir = File.createTempDir()
def csv = new File(dir, 'orders.csv')
// Write: text in one go
csv.text = '''id,customer,total
PO-1,Acme,250
PO-2,Globex,90
PO-3,Acme,1200
'''
// Append
csv << 'PO-4,Initech,40\n'
// Read all lines
def lines = csv.readLines()
println "${lines.size() - 1} data rows" // 4 data rows
// Read line by line (memory-friendly for big files), skipping the header
def total = 0
csv.eachLine { line, number ->
if (number == 1) return // header
def (id, customer, amount) = line.split(',')
total += amount.toInteger()
}
println "Total: $total" // 1580
// Parse into maps using the header
def header = lines.head().split(',')
def rows = lines.tail().collect { l -> [header, l.split(',')].transpose().collectEntries() }
println rows.findAll { it.customer == 'Acme' }*.id // [PO-1, PO-3]
// Write with a writer (closes automatically)
def report = new File(dir, 'report.txt')
report.withWriter('UTF-8') { w ->
rows.groupBy { it.customer }.each { cust, list -> w.writeLine("$cust: ${list.size()} order(s)") }
}
print report.text
// List and clean up files
dir.eachFile { println "file: ${it.name} (${it.length()} bytes)" }
dir.deleteDir()
println "cleaned up: ${!dir.exists()}"
Try it yourself
Given a folder of .csv files, print each file’s name and its number of data rows (excluding the header), largest first.
Show a solution
def dir = new File('exports')
dir.listFiles({ f -> f.name.endsWith('.csv') } as FileFilter)
.collect { [name: it.name, rows: it.readLines().size() - 1] }
.sort { -it.rows }
.each { println "${it.name}: ${it.rows}" }
Key takeaways
.text,<<andreadLines()cover most small-file needs.- Use
eachLinefor big files. withWriter/withReaderclose files automatically.- Use a real CSV parser for messy data.