Стандартная библиотека: Файлы и потоки
Ада предлагает различные средства для ввода/вывода файлов (I/O):
Текстовый ввод-вывод в текстовом формате, включая отображение информации на консоли.
Последовательный ввод-вывод в двоичном формате последовательным образом для конкретного типа данных.
Прямой ввод-вывод в двоичном формате для конкретного типа данных, но также поддерживающий доступ к любой позиции файла.
Потоковый ввод-вывод информации для нескольких типов данных, включая объекты неограниченных типов, с использованием файлов в двоичном формате.
В этой таблице представлено краткое описание функций, которые мы только что видели:
Опция ввода- вывода файлов |
Формат |
Произвольный доступ |
Типы данных |
---|---|---|---|
Text I/O |
текст |
строковый тип |
|
Sequential I/O |
двоичный |
одиночный тип |
|
Direct I/O |
двоичный |
да |
одиночный тип |
Stream I/O |
двоичный |
да |
несколько типов |
В следующих разделах мы подробно обсудим эти средства ввода-вывода.
Текстовый ввод-вывод
В большинстве примеров этого курса мы использовали процедуру Put_Line
для
отображения информации на консоли. Однако эта процедура также
принимает параметр File_Type
. Например, вы можете выбрать между стандартным
выводом (Standard_Output) и выводом стандартной ошибкой
(Standard_Error), явно задав этот параметр:
Вы также можете использовать этот параметр для записи информации в
любой текстовый файл. Чтобы создать новый файл для записи, используйте
процедуру Create
для инициализации объекта File_Type
,
который впоследствии можно передать в Put_Line
(вместо, например, Standard_Output
). После того, как вы закончите
запись информации, вы можете закрыть файл, вызвав процедуру Close
.
Вы используете аналогичный метод для чтения информации из текстового
файла. Однако при открытии файла вы должны указать, что это входной
файл (In_File
), а не выходной файл. Кроме того, вместо вызова процедуры
Put_Line
вы вызываете функцию Get_Line
для чтения информации
из файла.
Давайте посмотрим на пример, который записывает информацию в новый текстовый файл, а затем считывает ее обратно из того же файла:
В дополнение к процедурам Create
и Close
стандартная библиотека
также включает процедуру Reset
, которая, как следует из названия,
сбрасывает (стирает) всю информацию из файла. Например:
Запустив эту программу, мы замечаем, что, хотя мы записали первую
строку ("Hello World #1"
) в файл, она была удалена вызовом
процедуры сброса Reset
.
В дополнение к открытию файла для чтения или записи, вы также можете
открыть существующий файл для добавления в конец. Сделайте это, вызвав
процедуру Open
с параметром Append_File
.
При вызове процедуры открытия Open
возбуждает исключение, если
указанный файл не найден. Поэтому вы должны обрабатывать исключения в этом
контексте. В следующем примере удаляется файл, а затем предпринимается
попытка открыть тот же файл для чтения:
В этом примере файл создается вызовом Create
, а затем удаляется
вызовом Delete
. После вызова функции Delete
мы больше не можем
использовать объект File_Type
. После удаления файла мы пытаемся открыть
несуществующий файл, что возбуждает исключение Name_Error
.
Последовательный ввод-вывод
В предыдущем разделе представлена подробная информация о вводе/выводе
текстовых файлов. Здесь мы обсудим выполнение операций ввода-вывода для
файловов двоичного формата. Первый пакет, который мы рассмотрим -
это Ada.Sequential_IO
. Поскольку этот пакет является настраиваемым,
необходимо его настроить на тип данных ввод/вывлд которого мы будем
производить. После этого можно использовать те же процедуры, что и в предыдущем
разделе: Create
, Open
, Close
, Reset
и
Delete
. Однако вместо вызова процедур Get_Line
и Put_Line
следует вызвать процедуры Read
и Write
.
В следующем примере создается настройка пакета Ada.Sequential_IO
для
типов с плавающей запятой:
Мы используем один и тот же подход для чтения и записи сложной структурной информации. В следующем примере используется тип запись, включающая логическое значение типа Boolean и значение с плавающей запятой типа Float:
Как показывает пример, мы можем использовать тот же подход, который мы
использовали для типов с плавающей запятой, для выполнения файлового
ввода-вывода для этой записи. После того, как мы создадим экземпляр
пакета Ada.Sequential_IO
для типа записи, операции ввода-вывода файлов будут
выполняться таким же образом.
Прямой ввод-вывод
Прямой ввод-вывод доступен в пакете Ada.Direct_IO
. Этот механизм похож
на только что представленный последовательный ввод-вывод, но позволяет
нам получить доступ к любой позиции в файле. Создание пакета и
большинство операций очень похожи на последовательный ввод-вывод.
Чтобы переписать приложение Show_Seq_Float_IO
из предыдущего раздела
для использования пакета Ada.Direct_IO
, нам просто нужно заменить
Ada.Sequential_IO
на Ada.Direct_IO
в настройке пакета.
Это новый исходный код:
В отличие от последовательного ввода-вывода, прямой ввод-вывод
позволяет получить доступ к любой позиции в файле. Однако он не
предлагает возможность добавлять информацию в конец файла. Вместо этого он
предоставляет режим Inout_File
, позволяющий читать и записывать в файл
через один и тот же объект File_Type
.
Чтобы получить доступ к нужной позиции в файле, вызовите процедуру
Set_Index
и она установит новую позицию / индекс.
Вы можете использовать функцию Index
чтобы узать текущий индекс.
Посмотрим на пример:
Запустив этот пример, мы видим, что файл содержит значение 7.7
и не
содержит значения 6.7
, которое мы записали сначала. Мы перезаписали
значение, установив индекс на предыдущую позицию перед выполнением следующей
операции записи.
В этом примере мы использовали режим Inout_File
. Используя этот режим,
мы просто вернули индекс в начальное положение перед чтением из файла
(Set_Index (F, 1)
) вместо того, чтобы закрывать файл и повторно
открывать его для чтения.
Потоковый ввод-вывод
Все предыдущие подходы к файловому вводу-выводу в двоичном формате (последовательный и прямой ввод-вывод) работают с одним типом данных (тем, на который мы их настраиваем). Вы можете использовать эти подходы для записи объектов одного типа данных, хотя это может быть массивы или записи (потенциально со многими полями), но если вам нужно создать или обработать файлы, которые включают разные типы данных, либо объекты неограниченного типа, этих средств недостаточно. Вместо этого вы должны использовать потоковый ввод-вывод.
Потоковый ввод-вывод имеет некоторые общие черты с предыдущими
подходами. Мы по-прежнему используем процедуры Create
,
Open
и Close
. Однако вместо прямого доступа к файлу через объект
File_Type
вы используете тип Stream_Access
. Для
чтения и записи информации вы используете атрибуты 'Read
или
'Write
тех типов данных, которые вы читаете или пишете.
Давайте посмотрим на версию процедуры Show_Dir_Float_IO
из предыдущего
раздела. Процедура использует потоковый ввод-вывод вместо прямого ввода-вывода:
После вызова Create
мы получаем соответствующее значение
Stream_Access
, вызывая функцию Stream
(поток).
Затем мы используем этот поток для записи информации в файл
используя атрибут 'Write
типа Float
.
После закрытия файла и повторного открытия его для
чтения мы снова получаем значение Stream_Access
и используем его
для чтения информации из файла
с помощью атрибута 'Read
типа Float
.
Вы можете использовать потоки для создания и обработки файлов,
содержащих разные типы данных в одном файле. Вы также можете читать и
записывать неограниченные типы данных, такие как строки. Однако при
использовании неограниченных типов данных вы должны вызывать атрибуты
'Input
и 'Output
неограниченного типа данных: эти атрибуты
записывают информацию о границах или дискриминантах в дополнение
к фактическим данным объекта.
В следующем примере показан файловый ввод-вывод, который смешивает как строки разной длины, так и значения с плавающей запятой:
Когда вы используете потоковый ввод-вывод, в файл не записывается никакая информация, указывающая тип данных, которые вы записали. Если файл содержит данные разных типов, при чтении файла вы должны ссылаться на типы в том же порядке, что и при его написании. В противном случае полученная информация будет повреждена. К сожалению, строгая типизация данных в этом случае вам не поможет. Написание простых процедур для файлового ввода-вывода (как в приведенном выше примере) может помочь обеспечить согласованность формата файла.
Как и прямой ввод-вывод, поддержка потокового ввода-вывода также позволяет получить доступ к любому месту в файле. Однако при этом нужно быть предельно осторожным, чтобы положение нового индекса соответствовало ожидаемым типам данных.