Reading Files in Scala

Here’s the translation of the Go code for reading files to Scala, with explanations in Markdown format suitable for Hugo:

import scala.io.Source
import java.io.{File, FileInputStream, RandomAccessFile}
import java.nio.file.{Files, Paths}

// Reading files is a common task in many Scala programs.
// Let's look at some examples of reading files.

// This helper function will streamline our error handling
def check[T](expr: => T): T =
  try {
    expr
  } catch {
    case e: Exception => throw e
  }

// Let's start with the main function
@main def readingFiles(): Unit = {
  // Perhaps the most basic file reading task is
  // slurping a file's entire contents into memory.
  val dat = check(Files.readAllBytes(Paths.get("/tmp/dat")))
  print(new String(dat))

  // You'll often want more control over how and what
  // parts of a file are read. For these tasks, we can use
  // various Scala and Java APIs.

  // Read some bytes from the beginning of the file.
  // Allow up to 5 to be read but also note how many
  // actually were read.
  val fis = new FileInputStream("/tmp/dat")
  val b1 = new Array[Byte](5)
  val n1 = check(fis.read(b1))
  println(s"$n1 bytes: ${new String(b1, 0, n1)}")

  // You can also seek to a known location in the file
  // and read from there using RandomAccessFile.
  val raf = new RandomAccessFile("/tmp/dat", "r")
  check(raf.seek(6))
  val b2 = new Array[Byte](2)
  val n2 = check(raf.read(b2))
  println(s"$n2 bytes @ ${raf.getFilePointer - n2}: ${new String(b2, 0, n2)}")

  // Seeking relative to the current position
  check(raf.seek(raf.getFilePointer + 4))

  // and relative to the end of the file.
  check(raf.seek(raf.length - 10))

  // The Source from scala.io provides a convenient way
  // to read files line by line or as a whole.
  val source = Source.fromFile("/tmp/dat")
  val lines = source.getLines().toList
  println(s"Read ${lines.length} lines")
  source.close()

  // There is no built-in rewind, but we can
  // accomplish this by seeking to the start of the file.
  check(raf.seek(0))

  // We can use BufferedSource for efficient reading
  // of small chunks.
  val bufferedSource = Source.fromFile("/tmp/dat")
  val firstFive = bufferedSource.take(5).mkString
  println(s"5 bytes: $firstFive")
  bufferedSource.close()

  // Close the file handles when you're done
  fis.close()
  raf.close()
}

To run this program:

$ echo "hello" > /tmp/dat
$ echo "scala" >> /tmp/dat
$ scala reading-files.scala
hello
scala
5 bytes: hello
2 bytes @ 6: sc
Read 2 lines
5 bytes: hello

This Scala code demonstrates various ways to read files, including reading entire files, reading specific bytes, seeking to positions in files, and using buffered readers. It uses a combination of Java’s file I/O classes and Scala’s Source for different reading operations.

Next, we’ll look at writing files.