Функції для роботи з файлами надає модуль File.
Щоб прочитати весь файл, скористаймося File.read/1. Щоб записати у файл, скористаймося File.write/2.
Модуль також надає функції для копіювання, видалення, перейменування файлів тощо. Їхні назви схожі на відповідники з UNIX, наприклад:
File.cp/3 - скопіювати файл.File.rm/1 - видалити файл.File.rename/2 - перейменувати та/або перемістити файл.File.mkdir/1 - створити каталог.File.cwd/0 - отримати поточний робочий каталог.Усі згадані функції з модуля File також мають варіант із !, який викликає помилку замість повернення кортежу помилки (наприклад, File.read!/1). Скористаймося цим варіантом, якщо не плануємо обробляти помилки, як-от відсутні файли чи брак дозволів.
File.read("does_not_exist")
# => {:error, :enoent}
File.read!("does_not_exist")
# => ** (File.Error) could not read file "does_not_exist": no such file or directory
# (elixir 1.10.4) lib/file.ex:353: File.read!/1
Щоразу, коли ми записуємо у файл за допомогою File.write/2, відкривається дескриптор файлу і створюється новий процес Elixir. Тому запису у файл у циклі за допомогою File.write/2 варто уникати.
Натомість файл можна відкрити за допомогою File.open/2. Другий аргумент File.open/2 задає список режимів, який дає змогу вказати, для читання чи для запису відкривається файл.
Найуживаніші режими:
:read - відкрити для читання, файл має існувати:write - відкрити для запису, файл буде створено, якщо він не існує, наявний вміст буде перезаписано:append - відкрити для запису, файл буде створено, якщо він не існує, наявний вміст буде збереженоПовний перелік усіх режимів наведено в документації File.open/2.
File.open/2 повертає PID процесу, який працює з файлом. Щоб читати з файлу й записувати до нього, скористаймося функціями з модуля IO і передамо цей PID як пристрій IO.
Коли ми закінчили роботу з файлом, закриймо його за допомогою File.close/1.
file = File.open!("README.txt", [:write])
# => #PID<0.157.0>
IO.puts(file, "# README")
# => :ok
File.close(file)
# => :ok
Читання файлу за допомогою File.read/1 завантажить весь файл у памʼять одразу. Це може стати проблемою під час роботи з дуже великими файлами. Щоб ефективно їх обробляти, можна скористатися File.open/2 та IO.read/2, щоб читати файл рядок за рядком, або перетворити файл на потік за допомогою File.stream/3. Потік реалізує обидва протоколи, Enumerable і Collectable, а отже, його можна використовувати і для читання, і для запису.
File.stream!("file.txt")
|> Stream.map(&(&1 <> "!"))
|> Stream.into(File.stream!("new_file.txt"))
|> Stream.run()
Усі функції для роботи з файлами потребують шляху до файлу. Шляхи до файлів бувають абсолютними або відносними щодо поточного каталогу. Для роботи зі шляхами скористаймося функціями з модуля Path. Для кросплатформної сумісності створюймо шляхи за допомогою Path.join/1. Він обере розділювач, що відповідає платформі.
Path.expand(Path.join(["~", "documents", "important.txt"]))
"/home/user/documents/important.txt"