есть файлам (и вводу/выводу в общем), базам данных и устойчивым объектам.
Глава 10. Ввод/вывод и хранение данных
На чистом диске можно искать бесконечно.
Вычислительные машины хороши для вычислений. В этой тавтологии больше смысла, чем кажется на первый взгляд. Если бы программа только потребляла процессорное время да изредка обращалась к оперативной памяти, жизнь была бы куда проще.
Но от компьютера, занятого исключительно собой, мало толку. Рано или поздно придется получать информацию извне и отправлять ее во внешний мир, и вот тут-то жизнь перестает казаться медом.
Есть несколько факторов, затрудняющих ввод/вывод. Во-первых, ввод и вывод - совершенно разные вещи, но обычно мы мысленно объединяем их. Во-вторых, операции ввода/вывода столь же разнообразны, как и мир насекомых.
История знает такие устройства, как магнитные барабаны, перфоленты, магнитные ленты, перфокарты и телетайпы. Некоторые имели механические детали, другие были электромагнитными от начала и до конца. Одни позволяли только считывать информацию, другие — только записывать, а третьи умели делать и то и другое. Часть записывающих устройств позволяла стирать данные, другая — нет. Одни были принципиально последовательными, другие допускали произвольный доступ. На иных устройствах информация хранилась постоянно, другие были энергозависимыми. Некоторые требовали человеческого вмешательства, другие — нет. Есть устройства символьного и блочного ввода/вывода. На некоторых блочных устройствах можно хранить только блоки постоянной длины, другие допускают и переменную длину блока. Одни устройства надо периодически опрашивать, другие управляются прерываниями. Прерывания можно реализовать аппаратно, программно или смешанным образом. Есть буферизованный и небуферизованный ввод/вывод. Бывает ввод/вывод с отображением на память и канальный, а с появлением таких операционных систем, как UNIX, мы узнали об устройствах ввода/вывода, отображаемых на элементы файловой системы. Программировать ввод/вывод доводилось на машинном языке, на языке ассемблера и на языках высокого уровня. В некоторые языки механизм ввода/вывода жестко встроен, другие вообще не включают ввод/вывод в спецификацию языка. Приходилось выполнять ввод/вывод с помощью подходящего драйвера или уровня абстракции и без оного.
Возможно, все это показалось вам хаотичным нагромождением разнородных фактов; если так, вы абсолютно правы!.. Отчасти сложность проистекает из самой природы ввода/вывода, отчасти это результат компромиссов, принятых при проектировании, а отчасти следствие наследия прошлых лет, устоявшихся традиций и особенностей различных языков и операционных систем.
Ввод/вывод в Ruby сложен, потому что он сложен в принципе. Но мы старались описать его как можно понятнее и показать, где и когда стоит применять различные приемы.
В основе системы ввода/вывода в Ruby лежит класс IO
, который определяет поведение всех операций ввода/вывода. С ним тесно связан (и наследует ему) класс File
. В класс File
вложен класс Stat
, инкапсулирующий различные сведения о файле (например, разрешения и временные штампы). Методы stat
и lstat
возвращают объекты типа File::Stat
.
В модуле FileTest
также есть методы, позволяющие опрашивать практически те же свойства. Он подмешивается к классу File
, но может использоваться и самостоятельно.
Наконец, методы ввода/вывода есть и в модуле Kernel
, который подмешивается к классу Object
(предку всех объектов, включая и классы). Это простые процедуры, которыми мы пользовались на протяжении всей книги, не думая о том, от имени какого объекта они вызываются. По умолчанию они настроены на стандартный ввод и стандартный вывод.
Поначалу может показаться, что это хаотическое хитросплетение перекрывающейся функциональности. Но в каждый момент времени вам необходима лишь небольшая часть всего каркаса.
На более высоком уровне Ruby предлагает механизмы, позволяющие сделать объекты устойчивыми. Метод Marshal
реализует простую сериализацию объектов; он лежит в основе более изощренной библиотеки PStore
. Мы включили в эту главу и библиотеку DBM, хотя она умеет работать только со строками.
На самом высоком уровне возможен интерфейс с системами управления базами данных, например MySQL или Oracle. Эта тема настолько сложна, что ей можно было бы посвятить одну или даже несколько книг. Мы ограничимся лишь кратким введением. В некоторых случаях будут даны ссылки на архивы в сети.
10.1. Файлы и каталоги
Под
Класс File
тесно связан с классом IO
, которому наследует. Класс Dir
связан
10.1.1. Открытие и закрытие файлов
Метод класса File.new
, создающий новый объект File
, также открывает файл. Первым параметром, естественно, является имя файла.
Необязательный второй параметр называется'r'
, то есть только чтение. Ниже показано, как открывать файлы для чтения и записи.
file1 = File.new('one') # Открыть для чтения.
file2 = File.new('two', 'w') # Открыть для записи.
Есть также разновидность метода new, принимающая три параметра. В этом случае второй параметр задает начальные разрешения для файла (обычно записывается в виде восьмеричной константы), а третий представляет собой набор флагов, объединенных союзом ИЛИ. Флаги обозначаются константами, например: File::CREAT
(создать файл, если он еще не существует) и File::RDONLY
(открыть только для чтения). Такая форма используется редко.
file = File.new('three', 0755, File::CREAT|File::WRONLY)
В виде любезности по отношению к операционной системе и среде исполнения всегда закрывайте открытые вами файлы. Если файл был открыт для записи, то это не просто вежливость, а способ предотвратить потерю данных. Для закрытия файла предназначен метод close
:
out = File.new('captains.log', 'w')