File and IO
Elixir file operations, path manipulation, and IO (input/output) handling
File reading and writing
Reading files
# Read entire file (returns {:ok, binary} or {:error, reason})
case File.read("config.txt") do
{:ok, contents} -> process(contents)
{:error, :enoent} -> IO.puts("File not found")
{:error, reason} -> IO.puts("Error: #{inspect(reason)}")
end
# Bang version (raises on error)
contents = File.read!("config.txt")
# Read as lines
File.read!("data.txt") |> String.split("\n")
# Stream lines (lazy, memory-efficient for large files)
File.stream!("log.txt")
|> Stream.filter(&String.contains?(&1, "ERROR"))
|> Stream.map(&String.trim/1)
|> Enum.take(100)
# Read line by line with File.stream! + Enumerable
File.stream!("data.csv")
|> CSV.decode!()
|> Enum.each(fn row -> process(row) end)
# Read specific line
File.read!("file.txt") |> String.split("\n") |> Enum.at(4) # 0-indexed
# Read into UTF-8 string
{:ok, content} = File.read("file.txt")Writing files
# Write entire file (overwrites existing)
File.write("output.txt", "Hello, World!")
# Append to file
File.write("log.txt", "New entry\n", [:append])
# Write list of strings (more efficient than concatenation)
File.write!("output.txt", ["Line 1\n", "Line 2\n", "Line 3\n"])
# Write binary data
File.write!("image.bin", <<0, 1, 2, 3>>)File mode flags
| Flag | Meaning |
|---|---|
:read |
Open for reading (default) |
:write |
Open for writing |
:append |
Open for appending |
:exclusive |
Create new file, fail if exists |
:binary |
Open in binary mode |
:utf8 |
Open in UTF-8 mode |
:sync |
Sync to disk on each write |
Working with file handles
# Open file with explicit handle
{:ok, file} = File.open("data.txt", [:read, :utf8])
IO.readline(file) # read one line
IO.read(file, :all) # read entire contents
File.close(file)
# Using File.open with a function (auto-closes)
File.open("data.txt", [:read, :utf8], fn file ->
IO.read(file, :all)
end)Path manipulation
# Joining paths (OS-agnostic)
Path.join(["/home", "user", "documents", "file.txt"])
# => "/home/user/documents/file.txt"
# Extension and basename
Path.extname("photo.jpg") # => ".jpg"
Path.basename("dir/photo.jpg") # => "photo.jpg"
Path.basename("dir/photo.jpg", ".jpg") # => "photo"
Path.dirname("/home/user/file.txt") # => "/home/user"
# Expanding paths
Path.expand("~/documents") # => "/home/user/documents"
# Type checking
File.regular?("file.txt") # => true | false
File.dir?("/home/user") # => true | false
File.exists?("/tmp") # => true | falseDirectory operations
# Create directory (and parents)
File.mkdir_p("/tmp/my/app/data")
# List directory
File.ls("/tmp") # => {:ok, ["file1", "dir2"]}
# List directory recursively
File.ls_r("/tmp/my") # => {:ok, [list of all files/dirs]}
# Remove directory (must be empty)
File.rmdir("/tmp/empty_dir")
# Remove directory recursively
File.rm_rf("/tmp/my/app") # => {:ok, [list of deleted files]}File operations
# Copy
File.copy("source.txt", "dest.txt")
# Rename/move
File.rename("old.txt", "new.txt")
# Delete
File.rm("unwanted.txt")
# File stats
File.stat("file.txt") # => {:ok, %File.Stat{...}}
File.stat!("file.txt").size # => file size in bytes
# Touch (create empty file or update mtime)
File.touch!("new_file.txt")
# Change permissions
File.chmod("script.sh", 0o755)IO module
# Standard output
IO.puts("Hello") # with newline
IO.write("Hello") # without newline
IO.inspect(%{a: 1}) # pretty-print any term
# Inspect with options
IO.inspect(data, label: "DEBUG", limit: 5, printable_limit: 20)
# Standard error
IO.puts(:stderr, "Error message")
# Standard input
line = IO.gets("Enter name: ") # reads until newline
# Formatted output
:io.format("Hello ~s, you have ~B items~n", ["Alice", 5])StringIO
Use an in-memory string as a file-like device:
{:ok, io} = StringIO.open("")
IO.write(io, "hello ")
IO.write(io, "world")
{:ok, {_, content}} = StringIO.contents(io)
content # => "hello world"