Давайте предположим, что в нашей файловой системе есть файл example.txt, который содержит следующее:
- Привет! Ты из Китая, верно?
– Да, верно. Привет.
- Я слышал, что Китай был первой страной, которая приняла идеи коммунизма.
- Да, это правда. Мы пытаемся построить социалистическое общество с китайской спецификой.
- А что вы думаете об идеологии коммунизма?
- Я думаю, что коммунизм может быть хорошим идеалом, если будет правильно реализован. Он должен быть основан на равном распределении ресурсов и справедливости для всех людей.
- А что вы думаете о перестройке в России и распаде Советского Союза?
- Я думаю, что это был сложный период в истории России и СССР. Я уважаю решение народа России о переходе к рыночной экономике и демократии. Но я также считаю, что коммунизм может быть реализован лишь в том случае, если есть поддержка и понимание со стороны жителей страны.
Первое, что необходимо сделать для того, чтобы прочитать содержимое, это открыть файл. Открытие файла принимает 2 аргумента: путь до файла и права на этот файл
# В данном случае мы открываем наш файл по пути '/some/path/example.txt' с правами на чтение# результатом открытия файла будет обхект класса File содержащий значение файла, а также метаинформациюfile=File.open("/some/path/example.txt","r")
Можно выделить 3 основных права на файл:
r - read/чтение
a - append/добавление (мы можем добавить в файл значение, но не можем изменить уже существующее)
w - write/запись
Кроме того, права указаны в порядке суммирования, что означает, что каждый пункт будет помимо своих собственных прав иметь еще и права на из пункта выше.
После того как мы открыли файл нам доступны следующие методы:
read - перенос значения в строкове представление. При этом наши переносы строки, пробелы, табуляции сохраняются
readlines - перенос значения в массив строк, где каждая новая строка будет новым значением в массиве
each_line - возвращает объект Enumerator. Если открыть в нем блок кода, то можно проитерировать каждую строчку файла
readline - читает строку в файле и при следующем вызове этого же метода будет прочитана следующая строка
seek - перевод курсора к указанной позиции в файле. Принимает первым аргументом размер отступа, а вторым от чего именно будет отступ:
:END - с конца
:SET - с начала
:CUR - от текущей позиции
size - размер файла в байтах
birthtime - дата создания объекта
atime - дата последнего доступа к файлу
Важно понимать отличие между readlines и each_line с readline. reeadlines читает содержимое файла целиком и целиком помещает его в массив. Это может быть эффективно при работе с небольшими файлами, но если файл весит 1 ГБ, то весь 1ГБ будет помещен в память компьютера, а как мы знаем размеры памяти куда более ограничены по сравнению с постоянной памятью по типу SSD или HHD. each_line и readline отличается своей возможностью лениво читать файл. Если текущая итерация обрабатвает 1 строчку файла, то остальные строчки не будут помещены в память. Когда итерация начнет обрабатывать 2 строчку, то данные из первой строки очистятся из памяти, а данные из 3 и последующей не будут в нее загружены.
После завершения работы нам необходимо всегда закрывать работу файла вызвав у него метод close. Кроме того, мы можем не вызывать этот метод, если будет работать с файлом внутри блока при открытии, т.к. закрытие файла будет вызвано автоматически по окончанию работы блока кода:
Изменение файлов
Давайте сразу на примере посмотрим как мы можем записывать в файл:
Обратите внимание, в примере выше запись в файл всегда будет перезаписывать уже существующие данные. Для того, чтобы нам вместо перезаписи дополнять данными пользуются флагом a при записи
Такой же результат мы получим, если откроем файл с правами a
Помимо методов объекта существуют еще полезные методы класса, которые могут являться клонами метода объекта, либо дают новый функционал. Вот наиболее часто встречающиеся методы:
File.rename - переименновать файл
File.size - получить размер файла в байтах
File.exists? - существует ли файл
File.directory? - является ли файл директорией
File.file? - является ли файл файлом
File.foreach - такой же функционал как и у метода объекта .each_line
File.read - такой же функционал как и у метода объекта .read
File.readlines - такой же функционал как и у метода объекта .readlines
Также, методы класса самотоятельно открывают и закрывают файл, соответсвенно, нам об этом заботиться не нужно.
Работа с директорией
С файлами мы разобрались, но что делать, когда мы хотим работать сразу с несколькими файлами, объеденных в одной директории ? Для этого у нас есть класс Dir со своимсобственным функционалом. Вот несколько наиболее полезных классовых методов у него:
Dir.mkdir - создание новой директории. Внимание этот метод не создает вложнные каталоги. Если мы попытаемся создат ькаталог в несуществующем каталоге, то получим ошибку.
Dir.entries - возвращает массив названий файлов и каталогов в указаной директории. Учтите6 что это именно массив названий файлов, а не объектов File.
Dir.glob - Возвращает список файлов и директорий по паттерн матчингу. * обозначает любое количество символов, включая ноль символов. ** обозначает любое количество символов, включая вложенные каталоги. Кроме символа * также используются символы ? (заменяет один любой символ) и [] (определяет диапазон возможных символов). Например:
*.txt - соответствует всем файлам с расширением .txt в текущей директории.
docs/*.txt - соответствует всем файлам с расширением .txt в папке docs, находящейся в текущей директории.
**/*.txt - соответствует всем файлам с расширением .txt в любой вложенной директории, начиная с текущей.
Давайте посмотрим на пример использования:
Задание file_tree
Напишите метод file_hierachy(path) принимающий путь до директории. Выведите в консоль файлы в древовидной форме. Как это должно выглядеть в консоли:
Задание folder_size
Напишите метод calculate_folder_size(path), который принимает путь до директории и возвращает размер папки в гигабайтах, округленный до 2 символов после запятой
Рекоммендации:
Программы должна отлавливать все возможные ошибки (например, если передан несуществующий путь)
# выглядит точно так же как и обычное открытие файла, только файл помещается не в объект, а в параметр блока кода
File.open("/some/path/example.txt", "r") do |file|
# лениво итерируемся по каждой строчке файла, экономя память
file.each_line do |line|
puts line
end
end
# Заметьте, для записи файла используется уже другие права на работу с файлом
# Если файла на момент работы с ним не существует, то он будет создан
File.open("/some/path/another_example.txt", "w") do |file|
# Записываем в файл строку
file.write("some data")
end
# Заметьте, для записи файла используется уже другие права на работу с файлом
# Если файла на момент работы с ним не существует, то он будет создан
File.open("/some/path/another_example.txt", "w") do |file|
# Записываем в файл строку, старые данные остаются неизменными
file.write("some data", mode: "a")
end
File.open("/some/path/another_example.txt", "a") do |file|
# Записываем в файл строку, старые данные остаются неизменными
file.write("some data")
end
# Получение списка файлов в текущем каталоге
# Внимание, file_name это только название файла без полного пути
Dir.entries(".") do |file_name|
puts file_name
end
# Получение списка файлов в текущем каталоге и подкаталогах с расширением .rb
# Внимание, file_name это только название файла без полного пути
Dir.glob("**/*.rb") do |file_name|
puts file_name
end