157

For reading files in Scala, there is

Source.fromFile("file.txt").mkString

Is there an equivalent and concise way to write a string to file?

Most languages support something like that. My favorite is Groovy:

def f = new File("file.txt")
// Read
def s = f.text
// Write
f.text = "file contents"

I'd like to use the code for programs ranging from a single line to a short page of code. Having to use your own library doesn't make sense here. I expect a modern language to let me write something to a file conveniently.

There are posts similar to this, but they don't answer my exact question or are focused on older Scala versions.

For example:

Matthias Braun
  • 24,493
  • 16
  • 114
  • 144
smartnut007
  • 5,913
  • 5
  • 40
  • 50
  • See [this](http://stackoverflow.com/questions/4604237/how-to-write-to-a-file-in-scala) question. I agree with the highest rated answer - it's better to have your own personal library. – ffriend Jul 30 '11 at 00:50
  • 2
    for this case I don't agree that one has to write their own personal library. Usually, when you are writing small pieces of programs to do ad hoc things(maybe I just want to write it as a single page scala script or just in REPL ). Then accessing a personal library becomes a pain. – smartnut007 Jul 30 '11 at 20:43
  • Basically, looks like there is nothing in scala 2.9 for this at this point. Not sure how if i should keep this question open. – smartnut007 Aug 01 '11 at 19:04
  • 1
    If you search for java.io in the Scala source code, you won't find many occurrences, and even less for output operations, particularly the PrintWriter. So, until Scala-IO library becomes official part of Scala, we have to use pure Java, as shown by paradigmatic. – PhiLho Aug 29 '11 at 12:55
  • yeah, prob also need an scala-io thats compatible with jdk7 IO improvements. – smartnut007 Aug 29 '11 at 18:52
  • @smartnut007: What about simply adding your library to the system-wide classpath ($CLASSPATH)? – Blaisorblade Oct 09 '11 at 12:49
  • Why not use Commons.io fileutils. write – KIC Sep 06 '17 at 06:20

16 Answers16

172

It is strange that no one had suggested NIO.2 operations (available since Java 7):

import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

Files.write(Paths.get("file.txt"), "file contents".getBytes(StandardCharsets.UTF_8))

I think this is by far the simplest and easiest and most idiomatic way, and it does not need any dependencies sans Java itself.

aknuds1
  • 57,609
  • 57
  • 177
  • 299
Vladimir Matveev
  • 97,409
  • 27
  • 241
  • 264
  • This ignores newline character for some reason. – Kakaji Mar 12 '16 at 06:16
  • @Kakaji, could you please elaborate? I've just tested it with strings with newlines, and it works perfectly. It just cannot filter anything - Files.write() writes the byte array as a blob, without analyzing its contents. After all, in some binary data 0x0d byte may have important meaning other than newline. – Vladimir Matveev Mar 12 '16 at 07:46
  • 4
    Additional note: if you have a File, just '.toPath' to get the first parameter. – akauppi May 11 '16 at 08:35
  • 1
    This one is more industrial grade (due to the explicit CharSets choice) but lacks the simplicity (and one liner..) of the `reflect.io.File.writeAll(contents)` . Why? Three lines when you include the two import statements. Maybe the IDE does it for you automatically but if in the REPL it's not as easy. – StephenBoesch Jan 24 '17 at 21:11
  • 3
    @javadba we're in the JVM, imports pretty much don't count as 'lines' especially because the alternative is almost always adding a new library dependency. Anyway, `Files.write` also accepts a `java.lang.Iterable` as the second argument, and we can obtain that from a Scala `Iterable`, i.e. pretty much any collection type, using a converter: `import java.nio.file.{Files, Paths}; import scala.collection.JavaConverters.asJavaIterableConverter; val output = List("1", "2", "3"); Files.write(Paths.get("output.txt"), output.asJava)` – Yawar May 21 '18 at 01:04
84

Here is a concise one-liner using reflect.io.file, this works with Scala 2.12:

reflect.io.File("filename").writeAll("hello world")

Alternatively, if you want to use the Java libraries you can do this hack:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Andreas Neumann
  • 10,263
  • 1
  • 29
  • 52
Garrett Hall
  • 27,772
  • 10
  • 56
  • 73
  • 1
    +1 Works great. The `Some`/`foreach` combo is a bit funky, but it comes with the benefit of no try/catch/finally. – Brent Faust Mar 01 '13 at 02:33
  • 3
    Well if the write throws an exception you may want to close the file if you plan on recovering from the exception and reading/writing the file again. Fortunately scala provides one-liners for doing this as well. – Garrett Hall Mar 01 '13 at 15:25
  • 25
    This is not recommended since the scala.tools.nsc.io package is not part of the public API but used by the compiler. – Giovanni Botta Feb 04 '14 at 20:48
  • 3
    The `Some`/`foreach` hack is exactly why many people hate Scala for the unreadable code it causes hackers to produce. – Erik Kaplun Feb 16 '14 at 23:15
  • Doesn't work with Scala 2.10.4 . They remove the class. – Andreas Neumann May 26 '14 at 12:13
  • What encoding will this use? – rightfold Sep 10 '15 at 13:24
  • 3
    `scala.tootls.nsc.io.File` is an alias for the `reflect.io.File`. Still internal API, but at least a bit shorter. – kostja Sep 17 '15 at 11:59
  • 1
    The PrintWriter solution above isn't safe; i.e. the p.close might not every be called if p.write throws an exception. Additionally, your solution doesn't easily allow for adding a BufferedWriter. I have address those in a solution I documented on another similar thread: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:43
  • 1
    @kostja Thanks for that tip about `reflect.io.File` : I can now replace the many many locations I coded of `scala.tools.nsc.io` with the `reflect.io` which is more acceptable to Intellij and to reviewers. – StephenBoesch Jan 24 '17 at 21:07
84

A concise one line:

import java.io.PrintWriter
new PrintWriter("filename") { write("file contents"); close }
pietrop
  • 841
  • 1
  • 8
  • 25
LRLucena
  • 1,595
  • 13
  • 12
  • 15
    While this approach looks nice, it is neither exception-safe nor encoding-safe. If an exception happens in `write()`, `close` will never be called, and the file won't be closed. `PrintWriter` also uses the default system encoding, which is *very* bad for portability. And finally, this approach generates a separate class specifically for this line (however, given that Scala already generates tons of classes even for a simple code, this is hardly a drawback in itself). – Vladimir Matveev Aug 18 '15 at 20:19
  • Per the comment above, while this is a one liner, it is unsafe. If you want more safety while having more options around location and/or buffering the input, see the answer I just posted on a similar thread: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:41
  • 2
    For temporary debug-logging I used `new PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} }` – Randall Whitman May 02 '17 at 16:49
  • Stylistically it's probably better to use `close()`, I think – Casey Nov 15 '17 at 19:04
  • 1
    This is two lines and can easily be made into one line like this: `new java.io.PrintWriter("/tmp/hello") { write("file contents"); close }` – Philluminati Apr 03 '20 at 10:21
41

If you like Groovy syntax, you can use the Pimp-My-Library design pattern to bring it to Scala:

import java.io._
import scala.io._

class RichFile( file: File ) {

  def text = Source.fromFile( file )(Codec.UTF8).mkString

  def text_=( s: String ) {
    val out = new PrintWriter( file , "UTF-8")
    try{ out.print( s ) }
    finally{ out.close }
  }
}

object RichFile {

  implicit def enrichFile( file: File ) = new RichFile( file )

}

It will work as expected:

scala> import RichFile.enrichFile
import RichFile.enrichFile

scala> val f = new File("/tmp/example.txt")
f: java.io.File = /tmp/example.txt

scala> f.text = "hello world"

scala> f.text
res1: String = 
"hello world
andreas
  • 1,266
  • 1
  • 13
  • 33
paradigmatic
  • 39,013
  • 17
  • 85
  • 143
  • 2
    You don't ever call close on the Source.fromFile returned instance which means the resource isn't closed until it is GCed (Garbage Collected). And your PrintWriter isn't buffered, so it is using the tiny JVM default buffer of 8 bytes, potentially significantly slowing your IO. I have just created an answer on a similar thread that deals with these issues: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:46
  • 1
    You are right. But the solution I gave here works well for short-lived programs with small IO. I don't recommend it for server process or large data (as a rule of thumb, more than 500 MB). – paradigmatic Dec 15 '15 at 07:46
23
import sys.process._
"echo hello world" #> new java.io.File("/tmp/example.txt") !
xiefei
  • 6,313
  • 2
  • 23
  • 43
  • It doesn'T work for me in the REPL. No errormessage, but if I look at /tmp/example.txt there is no. – user unknown Feb 09 '12 at 01:28
  • @user unknown, Sorry for missing out the '!' at the end of the command, fixed now. – xiefei Feb 09 '12 at 03:55
  • Wonderful! Now that it works, I would like to know why. What is `#>`, what does the `!` do? – user unknown Feb 09 '12 at 04:21
  • There are some materials about `scala.sys.process` package in [this book](http://blog.typesafe.com/free-pdf-from-typesafe-scala-for-the-impatien-64715) – xiefei Feb 09 '12 at 06:01
  • 11
    This solution is __not__ portable! only works in *nix systems. – Giovanni Botta Jan 16 '14 at 23:08
  • 2
    This won't work for writing arbitrary strings. It will only work for short strings that you can pass as arguments to the command line `echo` tool. – Rich Apr 11 '14 at 08:56
17

A micro library I wrote: https://github.com/pathikrit/better-files

file.write("Hi!")

or

file << "Hi!"
pathikrit
  • 29,060
  • 33
  • 127
  • 206
  • 3
    Love your library! This question is one of the top hits when googling how to write a file with scala -- now that your project has gotten bigger, you might want to expand your answer a bit? – asachet Jun 06 '17 at 16:53
12

You can easily use Apache File Utils. Look at function writeStringToFile. We use this library in our projects.

RyuuGan
  • 766
  • 6
  • 10
  • 3
    I use it all the time too. If you read the question carefully I have already metnioned why I dont want to use a library. – smartnut007 Feb 09 '12 at 18:28
  • Without using a library, I have created a solution that handles exceptions during read/writes AND is able to be buffered beyond the tiny buffer defaults provided by the Java libraries: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:47
7

One also has this format, which is both concise and the underlying library is beautifully written (see the source code):

import scalax.io.Codec
import scalax.io.JavaConverters._

implicit val codec = Codec.UTF8

new java.io.File("hi-file.txt").asOutput.write("I'm a hi file! ... Really!")
Dibbeke
  • 458
  • 3
  • 9
7

This is concise enough, I guess:

scala> import java.io._
import java.io._

scala> val w = new BufferedWriter(new FileWriter("output.txt"))
w: java.io.BufferedWriter = java.io.BufferedWriter@44ba4f

scala> w.write("Alice\r\nBob\r\nCharlie\r\n")

scala> w.close()
Luigi Sgro
  • 609
  • 7
  • 18
  • 4
    Fair enough, but this "concise enough" doesn't classify as "one statement" :P – Erik Kaplun Feb 16 '14 at 23:14
  • This code eptimozes many of the perceived problems of Java. Unfortunately Scala doesn't consider IO important enough so the standard library doesn't include one. – Chris Aug 25 '14 at 03:02
  • 1
    The answer hides an orphaned resource issues with new FileWriter. If the new FileWriter succeeds, but the new BufferedWriter failes, the FileWriter instance is never seen again and remains hanging open until GCed (Garbage Collected), and may not be closed even then (due to the way finalize guarantees work in the JVM). I have written an answer to a similar question which addresses these issues: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:50
2

You can do this with a mix of Java and Scala libraries. You will have full control over the character encoding. But unfortunately, the file handles will not be closed properly.

scala> import java.io.ByteArrayInputStream
import java.io.ByteArrayInputStream

scala> import java.io.FileOutputStream
import java.io.FileOutputStream

scala> BasicIO.transferFully(new ByteArrayInputStream("test".getBytes("UTF-8")), new FileOutputStream("test.txt"))
stefan.schwetschke
  • 8,596
  • 1
  • 22
  • 29
  • You have an orphaned resource instance issue in your code. Since you are not capturing the instances prior to your call, if either throws an exception before the method you are calling has been passed the parameters, the resources which successfully instantiated won't every be closed until the GCed (Garbage Collected), and even then may not be due to the way GC guarantees work. I have written an answer to a similar question which addresses these issues: http://stackoverflow.com/a/34277491/501113 – chaotic3quilibrium Dec 14 '15 at 22:53
  • 1
    You are right and your solution is quite nice. But here the requirement was doing it in one line. And when you read carefully, I have mentioned the resource leak in my answer as a limiting that comes with this requirement and with my approach. Your solution is nice, but would not match that one line requirement. – stefan.schwetschke Dec 15 '15 at 04:58
2

UPDATE: I have since created a more effective solution upon which I have elaborated here: https://stackoverflow.com/a/34277491/501113

I find myself working more and more in the Scala Worksheet within the Scala IDE for Eclipse (and I believe there is something equivalent in IntelliJ IDEA). Anyway, I need to be able to do a one-liner to output some of the contents as I get the "Output exceeds cutoff limit." message if I am doing anything significant, especially with the Scala collections.

I came up with a one-liner I insert into the top of each new Scala Worksheet to simplify this (and so I don't have to do the whole external library import exercise for a very simple need). If you are a stickler and notice that it is technically two lines, it's only to make it more readable in this forum. It is a single line in my Scala Worksheet.

def printToFile(content: String, location: String = "C:/Users/jtdoe/Desktop/WorkSheet.txt") =
  Some(new java.io.PrintWriter(location)).foreach{f => try{f.write(content)}finally{f.close}}

And the usage is simply:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n")

This allows me to optionally provide the file name should I want to have additional files beyond the default (which completely overwrites the file each time the method is called).

So, the second usage is simply:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n", "C:/Users/jtdoe/Desktop/WorkSheet.txt")

Enjoy!

Community
  • 1
  • 1
chaotic3quilibrium
  • 5,066
  • 5
  • 48
  • 73
2

I know it's not one line, but it solves the safety issues as far as I can tell;

// This possibly creates a FileWriter, but maybe not
val writer = Try(new FileWriter(new File("filename")))
// If it did, we use it to write the data and return it again
// If it didn't we skip the map and print the exception and return the original, just in-case it was actually .write() that failed
// Then we close the file
writer.map(w => {w.write("data"); w}).recoverWith{case e => {e.printStackTrace(); writer}}.map(_.close)

If you didn't care about the exception handling then you can write

writer.map(w => {w.writer("data"); w}).recoverWith{case _ => writer}.map(_.close)
J_mie6
  • 698
  • 1
  • 8
  • 24
2

Use ammonite ops library. The syntax is very minimal, but the breadth of the library is almost as wide as what one would expect from attempting such a task in a shell scripting language like bash.

On the page I linked to, it shows numerous operations one can do with the library, but to answer this question, this is an example of writing to a file

import ammonite.ops._
write(pwd/'"file.txt", "file contents")
smac89
  • 26,360
  • 11
  • 91
  • 124
1

Here's the modern, safe one liner:

java.nio.file.Files.write(java.nio.file.Paths.get("/tmp/output.txt"), "Hello world".getBytes());

nio is a modern IO library shipped by default with the JDK 9+ so no imports or dependencies required.

Philluminati
  • 2,158
  • 2
  • 21
  • 27
0

os-lib is the best modern way to write to a file, as mentioned here.

Here's how to write "hello" to the file.txt file.

os.write(os.pwd/"file.txt", "hello")

os-lib hides the Java ugliness and complexity (it uses the Java libs under the hood, so it's just as performant). See here for more info about using the lib.

Powers
  • 12,561
  • 7
  • 60
  • 82
-1

Through the magic of the semicolon, you can make anything you like a one-liner.

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
val outfile = java.io.File.createTempFile("", "").getPath
val outstream = new PrintWriter(Files.newBufferedWriter(Paths.get(outfile)
  , StandardCharsets.UTF_8
  , StandardOpenOption.WRITE)); outstream.println("content"); outstream.flush(); outstream.close()
Ion Freeman
  • 402
  • 6
  • 15
  • No argument here. I decided not to make remembering which APIs need me to flush part of my life, so I just always do it. – Ion Freeman May 25 '18 at 22:27