Fi

File у Elixir

2 вправи

Про концепцію File

Функції для роботи з файлами надає модуль 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"
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію File

Практика заблокована

Розблокуйте ще 1 вправу, щоб практикувати концепцію File