What's a simple and canonical way to read an entire file into memory in Scala? (Ideally, with control over character encoding.)
The best I can come up with is:
scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_)
or am I supposed to use one of Java's god-awful idioms, the best of which (without using an external library) seems to be:
import java.util.Scanner
import java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()
From reading mailing list discussions, it's not clear to me that scala.io.Source is even supposed to be the canonical I/O library. I don't understand what its intended purpose is, exactly.
... I'd like something dead-simple and easy to remember. For example, in these languages it's very hard to forget the idiom ...
Ruby open("file.txt").read
Ruby File.read("file.txt")
Python open("file.txt").read()
val lines = scala.io.Source.fromFile("file.txt").mkString
By the way, "scala.
" isn't really necessary, as it's always in scope anyway, and you can, of course, import io's contents, fully or partially, and avoid having to prepend "io." too.
The above leaves the file open, however. To avoid problems, you should close it like this:
val source = scala.io.Source.fromFile("file.txt")
val lines = try source.mkString finally source.close()
Another problem with the code above is that it is horribly slow due to its implementation. For larger files one should use:
source.getLines mkString "\n"
Just to expand on Daniel's solution, you can shorten things up tremendously by inserting the following import into any file which requires file manipulation:
import scala.io.Source._
With this, you can now do:
val lines = fromFile("file.txt").getLines
I would be wary of reading an entire file into a single String
. It's a very bad habit, one which will bite you sooner and harder than you think. The getLines
method returns a value of type Iterator[String]
. It's effectively a lazy cursor into the file, allowing you to examine just the data you need without risking memory glut.
Oh, and to answer your implied question about Source
: yes, it is the canonical I/O library. Most code ends up using java.io
due to its lower-level interface and better compatibility with existing frameworks, but any code which has a choice should be using Source
, particularly for simple file manipulation.
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString
Java 8+
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}
val path = Paths.get("file.txt")
new String(Files.readAllBytes(path), StandardCharsets.UTF_8)
Java 11+
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
val path = Path.of("file.txt")
Files.readString(path, StandardCharsets.UTF_8)
These offer control over character encoding, and no resources to clean up. It's also faster than other patterns (e.g. getLines().mkString("\n")
) due to more efficient allocation patterns.
Files.readString(Path.of("file.txt"), StandardCharsets.UTF_8)
.
(EDIT: This does not work in scala 2.9 and maybe not 2.8 either)
Use trunk:
scala> io.File("/etc/passwd").slurp
res0: String =
##
# User Database
#
... etc
slurp
"? Have we truly ditched obvious, intuitive name? The problem with slurp
is that it might make sense after-the-fact, to someone with English as a first language, at least, but you would never think of it to begin with!
File
is no longer in 2.8.0, isn't it?
slurp
is fantastic! :) Was fantastic? I don't find it. ;(
I've been told that Source.fromFile is problematic. Personally, I have had problems opening large files with Source.fromFile and have had to resort to Java InputStreams.
Another interesting solution is using scalax. Here's an example of some well commented code that opens a log file using ManagedResource to open a file with scalax helpers: http://pastie.org/pastes/420714
Using getLines() on scala.io.Source discards what characters were used for line terminators (\n, \r, \r\n, etc.)
The following should preserve it character-for-character, and doesn't do excessive string concatenation (performance problems):
def fileToString(file: File, encoding: String) = {
val inStream = new FileInputStream(file)
val outStream = new ByteArrayOutputStream
try {
var reading = true
while ( reading ) {
inStream.read() match {
case -1 => reading = false
case c => outStream.write(c)
}
}
outStream.flush()
}
finally {
inStream.close()
}
new String(outStream.toByteArray(), encoding)
}
If you don't mind a third-party dependency, you should consider using my OS-Lib library. This makes reading/writing files and working with the filesystem very convenient:
// Make sure working directory exists and is empty
val wd = os.pwd/"out"/"splash"
os.remove.all(wd)
os.makeDir.all(wd)
// Read/write files
os.write(wd/"file.txt", "hello")
os.read(wd/"file.txt") ==> "hello"
// Perform filesystem operations
os.copy(wd/"file.txt", wd/"copied.txt")
os.list(wd) ==> Seq(wd/"copied.txt", wd/"file.txt")
with one-line helpers for reading bytes, reading chunks, reading lines, and many other useful/common operations
Just like in Java, using CommonsIO library:
FileUtils.readFileToString(file, StandardCharsets.UTF_8)
Also, many answers here forget Charset. It's better to always provide it explicitly, or it will hit one day.
One more: https://github.com/pathikrit/better-files#streams-and-codecs
Various ways to slurp a file without loading the contents into memory:
val bytes : Iterator[Byte] = file.bytes
val chars : Iterator[Char] = file.chars
val lines : Iterator[String] = file.lines
val source : scala.io.BufferedSource = file.content
You can supply your own codec too for anything that does a read/write (it assumes scala.io.Codec.default if you don't provide one):
val content: String = file.contentAsString // default codec
// custom codec:
import scala.io.Codec
file.contentAsString(Codec.ISO8859)
//or
import scala.io.Codec.string2codec
file.write("hello world")(codec = "US-ASCII")
For emulating Ruby syntax (and convey the semantics) of opening and reading a file, consider this implicit class (Scala 2.10 and upper),
import java.io.File
def open(filename: String) = new File(filename)
implicit class RichFile(val file: File) extends AnyVal {
def read = io.Source.fromFile(file).getLines.mkString("\n")
}
In this way,
open("file.txt").read
You do not need to parse every single line and then concatenate them again...
Source.fromFile(path)(Codec.UTF8).mkString
I prefer to use this:
import scala.io.{BufferedSource, Codec, Source}
import scala.util.Try
def readFileUtf8(path: String): Try[String] = Try {
val source: BufferedSource = Source.fromFile(path)(Codec.UTF8)
val content = source.mkString
source.close()
content
}
val content = source.mkString
Codec
. I got test fail on sbt test
because can't set it, while Intellij's test command pass all tests. And you can use def using
from this
The obvious question being "why do you want to read in the entire file?" This is obviously not a scalable solution if your files get very large. The scala.io.Source
gives you back an Iterator[String]
from the getLines
method, which is very useful and concise.
It's not much of a job to come up with an implicit conversion using the underlying java IO utilities to convert a File
, a Reader
or an InputStream
to a String
. I think that the lack of scalability means that they are correct not to add this to the standard API.
as a few people mentioned scala.io.Source is best to be avoided due to connection leaks.
Probably scalax and pure java libs like commons-io are the best options until the new incubator project (ie scala-io) gets merged.
you can also use Path from scala io to read and process files.
import scalax.file.Path
Now you can get file path using this:-
val filePath = Path("path_of_file_to_b_read", '/')
val lines = file.lines(includeTerminator = true)
You can also Include terminators but by default it is set to false..
For faster overall reading / uploading a (large) file, consider increasing the size of bufferSize
(Source.DefaultBufSize
set to 2048
), for instance as follows,
val file = new java.io.File("myFilename")
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2)
Note Source.scala. For further discussion see Scala fast text file read and upload to memory.
print every line, like use Java BufferedReader read ervery line, and print it:
scala.io.Source.fromFile("test.txt" ).foreach{ print }
equivalent:
scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}
in arguments you can give file path and it will return all lines
You can use
Source.fromFile(fileName).getLines().mkString
however it should be noticed that getLines() removes all new line characters. If you want save formatting you should use
Source.fromFile(fileName).iter.mkString
Success story sharing