Scala: work with files & directories
How often do you need to write or read files? This is pretty common task in any more or less not trivial project. Image upload process, CSV or XML parsing, XLS report generation etc. All these operations imply input or output processing. So I’m going to make an overview of the most powerful library for work with files in Scala.
Here is a list of operations which I want to cover:
- file creation / removing
- file writing / reading
- file comparison
- zipping / unzipping
Of course this list doesn’t limit number of useful functions which you can perform with files and directories.
The most suitable format for this article is tutorial + example, so I’ll try to type more lines of code and less bla-bla-bla. Any way if you want to discuss something, feel free to leave a comment.
Looking a little ahead I want to say that I will not write about native scala.io.Source
, instead I’ll show a powerful library better-files. The lib is written on top of Java NIO and has very transparent API.
Before we start, we need to add the library to a project. Here is a simple way to do this with SBT:
libraryDependencies += "com.github.pathikrit" %% "better-files" % "2.16.0"
Keep in mind that you need to use an appropriate version, because on the moment of writing 2.16.0 is the latest version.
The next step is to add 2 imports:
import better.files._ import better.files.File._
Finally we can start manipulations with files.
How to create a file / directory (handle if it’s not exist)
val file1: File = "/Users/Alex/Downloads/file1.txt" .toFile .createIfNotExists()
It’s so simple, isn’t it? After this code is executed, file1.txt is created by the following path /Users/Alex/Downloads/
Look at the createIfNotExists()
method. You can pass parameters into it. For example you want to create a directory.
val dir: File = "/Users/Alex/Downloads/dir" .toFile .createIfNotExists(true)
Passing true
into the method we force a dir folder creation by the path /Users/Alex/Downloads/
What about creation of folder with an extra path? Let’s assume, there are no directories in the /Users/Alex/Downloads/
val dir: File = "/Users/Alex/Downloads/extra/path/dir" .toFile .createIfNotExists(true, true)
The code snippet above creates three folders: extra, path and dir.
The same approach can be applied to the file creation. If you want create a parent folder(-s) for the file, simply set boolean
value as createParents
parameter.
How to write to a file
The most popular question which you may find on StackOverflow. At least I think so. With help of better-files you can write to files easily!
val html = (root/"Users/Alex/Downloads"/"test.html") .createIfNotExists() .append("<h1>") .appendLines("", "Header", "</h1>")
As you see two options are allowed: write one line and write multiple lines. Also you can write directly bytes in a file.
Another useful input operation is an overwriting. In the context of the previous example we can see how to write entirely new content to a file:
html.overwrite("<p>Just one paragraph</p>")
How to read a file
The most robust way to read all lines of text from a file is:
val simpleFile = (root/"Users/Alex/Downloads"/"simple_file.txt") .append("simple text") //simple text println(simpleFile.contentAsString)
Alright! Getting an entire content as a string is pretty simple. But what if we want to traverse through a content line by line? It’s also possible:
val simpleFile = (root/"Users/Alex/Downloads"/"simple_file.txt") .overwrite("") .appendLines("#1 line", "#2 line", "#3 line") simpleFile.lines.map(line => println(s"decor $line decor"))
Voila!
As I mentioned in the file writing section, you may want to work with bytes. Here is a small example of such approach:
val simpleFile = (root/"Users/Alex/Downloads"/"simple_file.txt") .overwrite("I'm from simple_file.txt") val destinationFile = (root/"Users/Alex/Downloads"/"test.txt") .write(simpleFile.byteArray)(OpenOptions.default) println(destinationFile.contentAsString)
Delete file / directory
Another group of popular questions is about delete operation. Let’s consider the most trivial example, when we need to delete a file when it exists:
val forDelete = (root/"Users/Alex/Downloads"/"file_to_delete.txt") .createIfNotExists() if (forDelete.exists) forDelete.delete()
The same method can be applied to a process of directory deletion. Furthermore it runs recursively and deletes all files and directories inside of a target folder.
How to compare files / folders
It’s pretty common scenario for developers. So what better-files library suggest for solving of this problem? Use ==
when you want to compare two files / directories by path. Use ===
when you want to check equality of two files / folders by content.
I guess, that comparison of two folders content was never so easy before 🙂
How to zip and unzip files / directories
In order to zip file or directory, just use following construction:
val forZip = (root/"Users/Alex/Downloads"/"gameboard.xlsx") forZip.zipTo(root/"Users/Alex/Downloads"/"archive.zip")
When you need to unzip an archive, simply call unzipTo
method.
Summary
I never thought, that work with files may be so easy as with better-files. In the article I showed the most popular operations, but with this library you can do much more. For example you can copy file or directory to some destination, work with file attributes (extension, size), set permissions, scan content…
Now you understand why this library works with files like a boss? 🙂
If you liked this blog post, share it in social networks!
Thanks!