Intermediate

I/O Operations in Julia

Learn console output, reading input, file reading and writing, formatted output, and I/O error handling in Julia with Docker-ready examples

Input and output are how a program talks to the outside world—the terminal, files on disk, network sockets, and pipes. In the Hello World tutorial you met println, but that was only the surface. Julia treats I/O through a uniform abstraction: everything readable or writable is a stream (an IO object), and the same functions—read, write, print, println—work on all of them thanks to multiple dispatch.

This uniformity is one of Julia’s quiet strengths. Because stdout, an open file, and an in-memory buffer are all just IO values, a function that writes to one can write to any of them without change. You will see this when we redirect output to stderr or a file simply by passing a different first argument.

In this tutorial you will learn how to write to the console with fine control, read input from the user, read and write files, produce formatted output with the Printf standard library, and handle the errors that inevitably arise when files go missing. As a language built for scientific computing, Julia leans on the do-block pattern to guarantee files are closed even when something goes wrong.

Console Output

println writes a value followed by a newline; print omits the newline. For anything more structured, the Printf standard library provides C-style format strings via the @printf and @sprintf macros. The show function prints a value’s programmatic representation (the form you would type to recreate it).

Create a file named console_output.jl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using Printf

# println adds a trailing newline; print does not
println("println adds a newline")
print("print does not")
print(" — see?\n")

# String interpolation with $
lang = "Julia"
version = 1.11
println("Language: $lang, version $version")

# Formatted output with @printf
@printf("Pi to 4 places: %.4f\n", π)
@printf("Padded integer: |%5d|\n", 42)
@printf("Hex: %x, Octal: %o\n", 255, 8)

# @sprintf returns a formatted string instead of printing it
label = @sprintf("%08.3f", 3.14159)
println("Formatted string: $label")

# show() prints the code representation of a value
show([1, 2, 3])
println()

# Any IO stream can be the target — here, standard error
println(stderr, "This message goes to standard error")

The last line demonstrates dispatch on streams: println(stderr, ...) sends text to stderr instead of stdout. The eight lines before it all print to stdout. Running this produces on standard output:

println adds a newline
print does not — see?
Language: Julia, version 1.11
Pi to 4 places: 3.1416
Padded integer: |   42|
Hex: ff, Octal: 10
Formatted string: 0003.142
[1, 2, 3]

Reading Console Input

readline() reads one line from standard input and returns it as a String (without the trailing newline). Since input always arrives as text, use parse to convert it to a number.

Create a file named greet_input.jl:

1
2
3
4
5
6
7
print("Enter your name: ")
name = readline()
println("Hello, $name!")

print("Enter a number: ")
n = parse(Int, readline())
println("Its square is $(n^2)")

Because the prompts use print (no newline) and the piped input is not echoed back, feeding Ada and 7 into the program produces:

Enter your name: Hello, Ada!
Enter a number: Its square is 49

When running interactively in a terminal, you would see your typed input appear between the prompt and the response.

Writing and Reading Files

The idiomatic way to work with a file is open(filename, mode) do file ... end. The do-block form passes the open stream to the block and automatically closes it when the block finishes—even if an error is thrown. Common modes are "w" (write, truncating), "a" (append), and "r" (read, the default).

Julia offers several ways to read back: read(path, String) slurps the whole file into a string, eachline(path) iterates lazily line by line, and readlines(path) returns a Vector of all lines.

Create a file named io_operations.jl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using Printf

# --- Writing a file (do-block auto-closes it) ---
open("notes.txt", "w") do file
    println(file, "Julia I/O Demo")
    println(file, "Line 2 of 3")
    println(file, "Line 3 of 3")
end
println("File written.")

# --- Reading the whole file at once ---
content = read("notes.txt", String)
println("\nFull contents:")
print(content)

# --- Reading line by line with enumerate for line numbers ---
println("\nNumbered lines:")
for (i, line) in enumerate(eachline("notes.txt"))
    @printf("%d: %s\n", i, line)
end

# --- Appending in "a" mode does not overwrite ---
open("notes.txt", "a") do file
    println(file, "Appended line")
end

lines = readlines("notes.txt")
println("\nTotal lines now: $(length(lines))")

I/O Error Handling

File operations can fail—the file may not exist, or permissions may be wrong. Julia signals these with a SystemError, which you catch using try/catch. You can also check ahead of time with isfile, and inspect a file with filesize.

Create a file named safe_read.jl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
filename = "missing.txt"

# Reading a nonexistent file throws a SystemError
try
    data = read(filename, String)
    println(data)
catch err
    if isa(err, SystemError)
        println("Error: could not open '$filename'")
    else
        rethrow(err)   # re-raise anything we didn't expect
    end
end

# Check existence before reading to avoid the error entirely
if isfile(filename)
    println("$filename is $(filesize(filename)) bytes")
else
    println("$filename is not present")
end

Running this yields:

Error: could not open 'missing.txt'
missing.txt is not present

Running with Docker

You can run every example without installing Julia locally.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pull the official image
docker pull julia:1.11-alpine

# Run the console output example
docker run --rm -v $(pwd):/app -w /app julia:1.11-alpine julia console_output.jl

# Run the file read/write example
docker run --rm -v $(pwd):/app -w /app julia:1.11-alpine julia io_operations.jl

# Run the error-handling example
docker run --rm -v $(pwd):/app -w /app julia:1.11-alpine julia safe_read.jl

# For the input example, add -i and pipe input into the container
printf "Ada\n7\n" | docker run --rm -i -v $(pwd):/app -w /app julia:1.11-alpine julia greet_input.jl

The -v $(pwd):/app mount lets the container write notes.txt back into your current directory, so you can inspect the file after the run.

Expected Output

Running io_operations.jl produces:

File written.

Full contents:
Julia I/O Demo
Line 2 of 3
Line 3 of 3

Numbered lines:
1: Julia I/O Demo
2: Line 2 of 3
3: Line 3 of 3

Total lines now: 4

Key Concepts

  • Everything is a streamstdout, stderr, open files, and in-memory buffers are all IO objects, so print, println, read, and write work uniformly on any of them via multiple dispatch.
  • The do-block closes files for youopen(name, mode) do file ... end guarantees the file is closed even if an exception is thrown, making it the safest and most idiomatic pattern.
  • Choose your read strategyread(path, String) loads everything at once, eachline(path) iterates lazily (ideal for large files), and readlines(path) returns a vector of lines.
  • Printf for formatted output@printf prints with C-style format specifiers, while @sprintf returns the formatted text as a String; both require using Printf.
  • Input is always textreadline() returns a String; use parse(Int, ...) or parse(Float64, ...) to convert numeric input.
  • Handle failures with try/catch — missing or unreadable files raise a SystemError; catch it, or guard with isfile before opening.
  • Redirect by argument, not by API — sending output to a file or to stderr is just a matter of passing a different first argument, with no separate stream API to learn.

Running Today

All examples can be run using Docker:

docker pull julia:1.11-alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining