Ruby/Работа с файлами

Работа с файлами править

Файлы в программах играют роль хранилищ, в которые можно записать любые объекты. В отличие от привычных нам объектов, файлы позволяют хранить данные даже тогда, когда программа завершила свою работу. Именно поэтому они могут использоваться для передачи данных между разными программами или разными запусками одной и той же программы.

 

Среди профессиональных программистов принято сохранять настройки программы в файлы, чтобы их мог исправить пользователь или конфигурационная программа.

 

В своих операционных системах фирма Microsoft ввела понятие «двоичный файл», но оно порождает больше проблем, чем удобств. Особенно при создании кроссплатформенных приложений.

Как организована работа с файлами? В самом общем случае работа с файлами состоит из следующих этапов:

  1. Открытие файла. Сущность этого этапа состоит в создании объекта класса File.
  2. Запись или чтение. Вызываются привычные нам методы вывода на экран и не совсем привычные — ввода-вывода.
  3. Закрытие файла. Во время закрытия файла происходят действия с файловой системой. С объектом, который создаётся при открытии файла, ничего не происходит, но после этого он указывает на закрытый файл, и производить операции чтения/записи при помощи него уже нельзя.

Существует масса способов реализации работы с файлами:

  1. Чтение при помощи класса IO. Класс IO имеет множество методов, которые позволяют производить чтение из текстовых файлов (с «двоичными файлами» лучше так не работать). Если нужно считать весь текстовый файл, то лучше пользоваться методами класса IO.
  2. Перенаправление потоков. Существует три предопределённые переменные: $stdout, $stdin и $stderr. Если им присвоить объект класса File (создаваемый во время открытия файла), то весь вывод пойдёт в файл, который присвоили переменной $stdout. Весь ввод будет браться из файла, который присвоили переменной $stdin, а все ошибки будут сохраняться в файле, который присвоили переменной $stderr. Если нужно работать только с одним файлом на чтение и одним файлом на запись, то обычно используют этот способ. Также очень удобно использовать перенаправление потока ошибок (переменная $stderr) для программ, которые работают в составе пакетных файлов и используют только интерфейс командной строки.
  3. Универсальный способ. Используется в ситуациях, когда нельзя использовать предыдущие два способа.

Подведём небольшой итог:

  1. Если нужно считать весь файл целиком, то надо использовать методы класса IO.
  2. Если нужно работать только с одним файлом на чтение и только одним файлом на запись, то надо использовать перенаправление потока.
  3. Если нельзя применить два вышеперечисленных способа, то надо использовать универсальный способ работы с файлами.

Чтение при помощи класса IO править

Для чтения файла целиком используется метод .read. Он считывает весь файл в виде строки. Во время его использования не сто́ит задумываться об открытии/закрытии файла, так как эти операции скрыты внутри метода.

config = IO.read('config.yaml')
config.class    #=> String

Имя файла — это строка.

В примере можно увидеть, как считывается файл config.yaml в переменную config. Вторая строка показывает, что при использовании метода .read файл считывается в виде строки (класс String). Теперь к переменной config можно применять любые методы из богатого строкового арсенала.

В качестве упражнения предлагаю вам считывать и выводить на экран любые файлы, которые вы только найдёте у себя на диске.

 

При считывании «двоичных файлов» в операционных системах фирмы Microsoft использовать данный способ нельзя, так как файл будет считан не до конца. Следует использовать универсальный способ работы с файлами.

Перенаправление потока править

Очень часто программист проектирует программу таким образом, чтобы ввод данных осуществлялся с клавиатуры. После сотни-другой циклов отладки программист так устаёт вводить данные, что создаёт файл, который содержит все необходимые входные данные, и перенаправляет на него поток ввода с клавиатуры, добавляя всего одну строчку в начало своей программы:

$stdin = File.open('входные данные.txt')

А вот другая история. Программист пишет и отлаживает программу, которая все необходимые данные выводит на экран. Но в конечном итоге программа должна запускаться без участия человека, и её вывод нужно сохранять в файл для дальнейшей обработки. Переписывать всю программу лень, и поэтому в начало своей программы он вставляет парочку волшебных строчек:

$stdout = File.open('выходные данные.txt', 'w')
$stderr = File.open('сообщения об ошибках.txt', 'a')

Вторым параметром метода .open передаётся модификатор доступа, то есть кодовое слово, по которому метод .open может предположить то, что вы будете делать с этим файлом. В нашем примере мы использовали модификатор w (англ. write — писать), который говорит о том, что мы будем только писать в файл. Причём каждый раз файл будет перезаписываться. При помощи модификатора a (англ. append — добавлять) мы указываем, что мы будем добавлять данные в файл, а не перезаписывать, как в случае с w.

После этого весь вывод на экран и сообщения об ошибках записываются в соответствующие файлы. Для того, чтобы посмотреть пример в действии, предлагаю вам выполнить следующую программу:

$stdout = File.open('выходные данные.txt', 'w')
$stderr = File.open('сообщения об ошибках.txt', 'a')

puts 'Очень важные данные,'
puts 'которые будут сохранены в файл'
raise 'Принудительно вызываем ошибку'

Метод raise — для принудительного вызова ошибки.

Теперь можете смело экспериментировать. Для начала, попробуйте поменять модификаторы w и a местами.

Универсальный способ работы с файлами править

Универсальным способом я назвал способ с использованием метода File.open. Дело в том, что при помощи него можно осуществлять не только считывание, запись и перезапись, но и закрытие файлов (чего нельзя сделать при использовании способа с переменными $stdout, $stdin и $stderr). Это позволяет несколько раз (за время выполнения программы) осуществлять операции открытия-закрытия файла. В виду того, что эта возможность нужна далеко не всегда, то и используется этот способ только тогда, когда использование всех предыдущих невозможно. Чтение из файла входные данные.txt при помощи универсального метода будет выглядеть следующим образом:

string = File.open('входные данные.txt', 'r'){ |file| file.read }

Модификатор доступа r указывать необязательно, так как он устанавливается по умолчанию. Поэтому следующий код тоже верен:

string = File.open('входные данные.txt'){ |file| file.read }

Если необходимо записать данные, то нужно использовать модификатор доступа a (добавление к концу файла) или w (запись в файл с его предварительной очисткой). Запись данных в файл осуществляется методами puts, write и так далее.

File.open('выходные данные.txt', 'w'){ |file| file.write string }
File.open('выходные данные.txt', 'a'){ |file| file.puts  string }

Замыкание метода .open (то есть фигурные скобки) нужен для того, чтобы при выходе из замыкания автоматически осуществлять закрытие файла.

Удаление файла править

Удаление файла осуществляется при помощи метода delete класса File. Например:

File.delete("example.txt")  #=> Файл был удалён (не помещён в корзину, как во многих операционных системах!)