The Scala library that provides extension methods for java.io and java.nio.
To get started, add little-io as a dependency to your project:
libraryDependencies += "com.github.losizm" %% "little-io" % "9.0.0"
NOTE: Starting with 5.0, little-io is written for Scala 3. See previous releases for compatibility with Scala 2.12 and Scala 2.13.
Here's a taste of what little-io offers.
If you have a File
, you can easily get and set its content.
import little.io.{ FileMethods, IoStringMethods }
val file = "greeting.txt".toFile
// Set file content to string
file.setText("Hello, world!")
// Get file content as string
println(file.getText()) // Hello, world!
And, if you need to append content, here's how you would do that:
import little.io.{ FileMethods, IoStringMethods }
// Append text and return reference to file
val file = "greeting.txt".toFile << "Hello, world!"
println(file.getText())
The same applies to java.nio.file.Path
.
import little.io.{ PathMethods, IoStringMethods }
val path = "greeting.txt".toPath << "Hello, world!"
println(path.getText())
And, if you prefer working with bytes, there are extension methods for those too.
import little.io.{ FileMethods, IoStringMethods }
val file = "greeting.txt".toFile
val data = "Hello, world!".getBytes("utf-8")
file.setBytes(data)
file << "\n" << data.reverse
println(String(file.getBytes(), "utf-8"))
If you have a File
or Path
, you can open an OutputStream
or Writer
to
write its content, and open an InputStream
or Reader
to read its content,
all with automatic resource management.
import little.io.{ FileMethods, IoStringMethods, WriterMethods }
val file = "numbers.txt".toFile
// Manages writer resource
file.withWriter { out =>
out write "One\n"
out writeLine "Two"
out << "Three\n"
}
// Manages reader resource
file.withReader { in =>
println(in.readLine())
println(in.readLine())
println(in.readLine())
}
If you'll be reading file content line by line, there's an even simpler way.
import little.io.{ FileMethods, IoStringMethods }
// Open file, print each line, and close file
"numbers.txt".toFile.forEachLine(println)
There are other methods for processing files line by line. You can filter and map the lines in a file to build a collection, or you can fold them to a single value.
import little.io.*
val file = "test.txt".toFile << "abc\n123\nxyz\n789"
// Filter lines containing digits only
val filtered = file.filterLines(_.matches("\\d+"))
assert { filtered == Seq("123", "789") }
// Map lines to uppercase
val mapped = file.mapLines(_.toUpperCase)
assert { mapped == Seq("ABC", "123", "XYZ", "789") }
// Fold lines to single, concatenated string
val folded = file.foldLines("") { _ + _ }
assert(folded == "abc123xyz789")
If you have a File
or Path
to a directory, you can map the files in the
directory. Or you can fold them to generate a single value.
import little.io.{ FileMethods, IoStringMethods }
val home = sys.props("user.home").toFile
// Get file names
val fileNames = home.mapFiles(_.getName)
// Calculate disk usage
val totalSize = home.foldFiles(0L) { _ + _.length }
A feature available since Java 7 is builtin library support for walking a file
tree starting at a particular Path
. This is carried out by specifying a
FileVisitor
as a callback to handle a set of events.
little-io makes this feature a little more Scala-like. You make a method
call to a Path
extension method, passing in a PartialFunction
to handle
events of interest.
import java.nio.file.FileVisitResult
import little.io.{ FileVisitEvent, IoStringMethods, PathMethods }
import FileVisitEvent.{ PreVisitDirectory, VisitFile }
// Traverse directories starting at 'src'
"./src".toPath.withVisitor {
// Go deeper if not 'test' directory
case PreVisitDirectory(dir, attrs) =>
if dir.getFileName.toString == "test" then
FileVisitResult.SKIP_SUBTREE
else
println(s"Listing files in ${dir.getFileName} directory...")
FileVisitResult.CONTINUE
// Print file name and size
case VisitFile(file, attrs) =>
println(s"${file.getFileName} is ${attrs.size} bytes.")
FileVisitResult.CONTINUE
}
Another feature available since Java 7 is WatchService
, which allows you to
monitor a directory for changes. You can poll the service to check for new,
modified, and deleted files.
With pure Java, you create a Path
to a directory, create a WatchService
using a reference to the path's FileSystem
, and then register the path with
the service while specifying the kinds of WatchEvent
you wish to track. A
WatchKey
is returned when the path is registered, and you use this key to poll
for file events.
With little-io, it's straight to the point.
import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE
import little.io.{ PathMethods, IoStringMethods }
val dir = "/tmp".toPath
// Print message when file is created
val handle = dir.withWatcher(ENTRY_CREATE) { evt =>
println(s"${evt.context} was created.")
}
try
Thread.sleep(60 * 1000)
finally
handle.close() // Close handle when finished
The Compressor
object provides various compression methods. For example, you
can compress a file using gzip
.
import java.io.File
import little.io.BufferSize
import little.io.Compressor.gzip
// Specify buffer size for I/O operations
given BufferSize = BufferSize(1024)
// Specify input and output files
val in = File("/path/to/file.txt")
val out = File("/path/to/file.txt.gz")
// Gzip input to output
gzip(in, out)
And decompress it with gunzip
.
import java.io.File
import little.io.BufferSize
import little.io.Compressor.gunzip
// Specify buffer size for I/O operations
given BufferSize = BufferSize(1024)
// Specify input and output files
val in = File("/path/to/file.txt.gz")
val out = File("/path/to/file.txt")
// Gunzip input to output
gunzip(in, out)
Or, to build an archive, you can zip
a directory.
import java.io.{ File, FileFilter }
import little.io.Compressor.zip
// Specify input directory and output file
val in = File("./src")
val out = File("/tmp/src.zip")
// Set file filter
given FileFilter with
def accept(f: File) =
f.isDirectory || f.getName.endsWith(".scala")
// Zip .scala files in all directories
zip(in, out)
And extract the files to a directory using unzip
.
import java.io.File
import little.io.AcceptAnyFile
import little.io.Compressor.unzip
// Specify input file and output directory
val in = File("/tmp/src.zip")
val out = File("/tmp/src")
// Unzip all files
unzip(in, out)(using AcceptAnyFile)
See scaladoc for additional details.
little-io is licensed under the Apache License, Version 2. See LICENSE for more information.