How do I read a text file in EK9?

← File I/O · Ref: Q151

EK9 provides the TextFile type for reading and writing text files. Create a TextFile with a path string, then use try-with-resources to open an input stream and read the contents safely.

CREATING A TEXTFILE

Create a TextFile by passing an ABSOLUTE file path as a String or a FileSystemPath:

  file <- TextFile("/tmp/data.txt")
  fromPath <- TextFile(FileSystemPath().inCurrentWorkingDirectory("data.txt"))

A relative name is never resolved against the working directory for you: TextFile("data.txt") is always unset. Anchor it with inCurrentWorkingDirectory (or inTemporaryDirectory) as above.
The file is not opened until you call input() or output(). But a TextFile is only SET when its path is absolute, exists and is a file: TextFile of a missing path is unset, so $file, file.isReadable() and file.input() are all unset too (and printing them prints nothing).

MAKING SURE THE FILE EXISTS

FileSystemPath.createFileIfAbsent() creates an empty file if needed. It returns a Result that must be checked:

  path <- FileSystemPath().inCurrentWorkingDirectory("data.txt")
  created <- path.createFileIfAbsent()
  if created.isOk()
    file <- TextFile(path)

READING WITH INPUT STREAM

Use file.input() to get a CloseableStringInput stream:

  try
    -> input <- file.input()
    if input?
      cat input > stdout

The input stream provides lines from the file one at a time. You can pipe them through stream operations or consume them directly.

TRY WITH RESOURCES

Always open file streams inside a try block. EK9's try-with-resources automatically closes the stream when the block exits, whether normally or via exception. This prevents resource leaks.

HANDLING FILE NOT FOUND

If the file does not exist, the TextFile is unset and file.input() returns an unset stream. A try-with-resources declaration (try -> input <- file.input()) is NOT a guard: the body still runs with the unset stream. Check input? inside the try (or check file? before it) before reading.

See Q3 for printing output. See Q4 for reading stdin. See Q134 for try/catch. See Q152 for writing files. See Q153 for file path exists. See Q154 for read file lines.

Example

defines module qa.fileio.read

  defines function

    processLine() as pure
      -> line as String
      <- rtn as String: "[read] " + line

  defines program

    ReadTextFileDemo()
      stdout <- Stdout()

      // === A MISSING FILE GIVES AN UNSET TEXTFILE ===

      missing <- TextFile("/tmp/qa-example-not-there.txt")
      stdout.println(`Missing file set: ${missing?}`)

      // === MAKE SURE THE FILE EXISTS ===

      // createFileIfAbsent returns a Result that must be checked
      path <- FileSystemPath("/tmp/qa-example-input.txt")
      created <- path.createFileIfAbsent()
      stdout.println(`File ready: ${created.isOk()}`)

      // === CREATING A TEXTFILE ===

      file <- TextFile(path)
      stdout.println("File: " + $file)

      // === TEXTFILE PROPERTIES ===

      stdout.println("Is readable: " + $file.isReadable())

      // Give the file some content to read back
      try
        -> output <- file.output()
        if output?
          output.println("first line")
          output.println("second line")

      // === READING WITH TRY-WITH-RESOURCES ===

      // The try body runs even if input() is unset, so check input? before reading
      try
        -> input <- file.input()
        if input?
          cat input | map with processLine > stdout

      // === THE BODY STILL RUNS FOR A MISSING FILE ===

      try
        -> missingInput <- missing.input()
        stdout.println(`Missing input set: ${missingInput?}`)

Common mistakes

E50060 — TextFile has no toString() method. Use the $ prefix operator for string conversion. See ek9 -h E50060 for details.

Incorrect:

stdout.println(file.toString())

Correct:

stdout.println("File: " + $file)

E50060 — TextFile has no canRead() method. Use isReadable() to check if the file can be read. EK9 uses descriptive method names. See ek9 -h E50060 for details.

Incorrect:

stdout.println("Is readable: " + $file.canRead())

Correct:

stdout.println("Is readable: " + $file.isReadable())
Other ways to ask this
  • How do I open and read a file in EK9?
  • What is TextFile in EK9?
  • How do I load file contents in EK9?

Coming from another language?

Java: Files.readString(Path) or BufferedReader with try-with-resources. Python: with open('file') as f: content = f.read(). Rust: fs::read_to_string('file'). Go: os.ReadFile('file'). EK9: TextFile(absolutePath) - anchor a relative name with FileSystemPath().inCurrentWorkingDirectory('file') - with try -> input <- file.input() for automatic resource management; a missing file gives an unset TextFile and an unset stream, so check input? inside the try.

Keywords: read, load, textfile, input, text, open, write, file, stream, content