│
العربية (ar) │
English (en) │
español (es) │
suomi (fi) │
français (fr) │
日本語 (ja) │
русский (ru) │
中文(中国大陆) (zh_CN) │
中文(台灣) (zh_TW) │
Введение
Каждый программист должен знать, как работать с файлами. Файлы используются для сохранения данных, т. е. в качестве хранилища данных, таким образом, что они могут быть получены в любой момент, без их воссоздания. Файлы можно использовать для сохранения пользовательских настроек, журналов ошибок, измерение или вычисление результатов, и многое другое. В данном разделе описываются основы использования файлов.
Процедурный стиль
Это довольно старый стиль, использующейся ещё во времена, когда Pascal не был объектно-ориентированным языком. Суть его в том, что задается тип файла, определяющий, какие будут храниться в нем данные. Для этого, используется конструкция вида: file of <тип данных>, где <тип данных> — название типа, который хранит в себе файл. Помимо стандартных типов (integer, extended, char и т.д.), существует особый тип — TextFile. Он определят, что каждая строка заканчивается специальным(ми) символом(ами) конца строки (См. LineEnding). Эти файлы могут быть открыты и отредактированы внутри среды Lazarus или в любом другом текстовом редакторе.
Ниже представлены примеры создания собственных типов файлов:
... type TIntegerFile = file of integer; // Позволяет писать только целые числа в файл TExtendedFile = file of extended; // Позволяет писать только дробные цифры в файл TCharFile = file of char; // Позволяет писать только одиночные символы в файл
Обработка ошибок ввода/вывода
Параметр компилятора обработки ошибок ввода/вывода указывает, как должна вести себя программа при возникновении ошибки в процессе работы с файлами: вызвать исключение или хранить результат операции ввода/вывода в специальной переменной IOResult.
Это задаётся с помощью специальной директивы компилятора:
{$I+} // В случаи ошибки будет вызвано исключение EInOutError (по умолчанию) {$I-} // Подавлять ошибки ввода-вывода: проверьте переменную IOResult для получения кода ошибки.
В случаи подавления ошибок ввода-вывода ({$I-}) результат операции с файлом будет храниться в переменной IOResult типа cardinal (числовой тип). Каждое число, хранимое в IOResult определяет тип возникшей ошибки(подробнее: [1]).
Процедуры работы с файлами
Эти процедуры и функции находятся в модуле system. Для более подробной информации смотрите документацию FPC:
ссылка на модуль 'System'.
- AssignFile (не допускайте использование процедуры Assign) — Связывает переменную с файлом
- Append — Открывает существующий файл для записи данных в конец и их редактирования
- BlockRead — Чтение данных из не типизированного файла в память
- BlockWrite — Запись данных из памяти в не типизированный файл
- CloseFile (не допускайте использование процедуры Close) — Закрыть открытый файл
- EOF — Проверка наличия конца файла
- Erase — Стереть файл с диска
- FilePos — Получить позицию в файле
- FileSize — Получить размер файла
- Flush — Записать файловый буфер на диск
- IOResult — Возвращает результат последней операции вводавывода
- Read — Считать из текстового файла
- ReadLn — Считать из текстового файла и перейти к следующей строке
- Reset — Открыть файл для чтения
- Rewrite — Создать и открыть файл для записи
- Seek — Изменить позицию в файле
- SeekEOF — Переместить позицию в файле в его конец
- SeekEOLn — Переместить позицию в файле в конец строки
- Truncate — Удалить все данные, после текущей позиции
- Write — Записать переменную в файл
- WriteLn — Записать переменную в текстовый файл и перейти к новой строке
Пример
Пример работы с текстовым файлом (тип TextFile):
program CreateFile; uses Sysutils; const C_FNAME = 'textfile.txt'; var tfOut: TextFile; begin // Связываем имя файла с переменной AssignFile(tfOut, C_FNAME); // Использовать исключение для перехвата ошибок (это по умолчанию и указывать не обязательно) {$I+} // Для обработки исключений, используем блок try/except try // Создать файл, записать текст и закрыть его. rewrite(tfOut); writeln(tfOut, 'Пример текстового файла!'); writeln(tfOut, 'Пример записи числа: ', 42); CloseFile(tfOut); except // Если ошибка - отобразить её on E: EInOutError do writeln('Ошибка обработки файла. Детали: ', E.ClassName, '/', E.Message); end; // Выводим результат операции и ожидаем нажатие Enter writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.'); readln; end.
Теперь откройте файл в любом текстовом редакторе и вы увидите, что пример текста, записан в нем!
Вы можете проверить работу обработки ошибок, установив атрибут файла «только для чтения» и снова запустив программу.
Обратите внимание, что в примере используется блок try/except. Данный способ позволяет выполнять несколько операций с файлами и использовать обработку исключений.
Вы также можете использовать режим {$I-}, но тогда вам придется проверять переменную IOResult после каждой операции с файлами для контроля ошибок.
Ниже приведен пример записи текста в конец файла:
program AppendToFile; uses Sysutils; const C_FNAME = 'textfile.txt'; var tfOut: TextFile; begin // Связываем имя файла с переменной AssignFile(tfOut, C_FNAME); // Для обработки исключений, используем блок try/except try // Открыть файл для записи в конец, записать текст и закрыть его. append(tfOut); writeln(tfOut, 'Ещё пример текстового файла!'); writeln(tfOut, 'Результат 6 * 7 = ', 6 * 7); CloseFile(tfOut); except on E: EInOutError do writeln('Ошибка обработки файла. Детали: ', E.Message); end; // Выводим результат операции и ожидаем нажатие Enter writeln('Файл ', C_FNAME, ' возможно содержит больше текста. Нажмите ВВОД для выхода.'); readln; end.
Чтение текстового файла:
program ReadFile; uses Sysutils; const C_FNAME = 'textfile.txt'; var tfIn: TextFile; s: string; begin // Вывод некой информации writeln('Чтение содержимого файла: ', C_FNAME); writeln('========================================='); // Связываем имя файла с переменной AssignFile(tfIn, C_FNAME); // Для обработки исключений, используем блок try/except try // Открыть файл для чтения reset(tfIn); // Считываем строки, пока не закончится файл while not eof(tfIn) do begin readln(tfIn, s); writeln(s); end; // Готово. Закрываем файл. CloseFile(tfIn); except on E: EInOutError do writeln('Ошибка обработки файла. Детали: ', E.Message); end; // Выводим результат операции и ожидаем нажатие Enter writeln('========================================='); writeln('Файл ', C_FNAME, ' считан. Нажмите ВВОД для выхода.'); readln; end.
Объектный стиль
В дополнение к старому методу обработки файлов, упомянутому выше процедурному стилю, существует новая система, которая использует концепции потоков (данных) на более высоком уровне абстракции. Это означает, что данные могут считываться или записываться в любом месте (диск, память, аппаратные порты и т. д.) через один универсальный интерфейс.
Кроме того, большинство классов обработки строк, могут иметь возможность загружать/сохранять содержимое из/в файл. Эти методы обычно называются SaveToFile и LoadFromFile.
Двоичные файлы
Для прямого доступа к файлам, так же удобно использовать класс TFileStream. Этот класс представляет собой инкапсуляцию системных процедур FileOpen, FileCreate, FileRead, FileWrite, FileSeek и FileClose, расположенных в модуле SysUtils.
Процедуры ввода-вывода
В приведенном ниже примере обратите внимание, что обработка действий с файлом, расположена внутри блока try..except. Поэтому обработка ошибок происходит так же, как в случаи использования процедур работы с файлами в классическом Pascal.
program WriteBinaryData; {$mode objfpc} uses Classes, Sysutils; const C_FNAME = 'binarydata.bin'; var fsOut : TFileStream; ChrBuffer: array[0..2] of char; begin // Создать некоторые случайные данные, которые будут храниться в файле ChrBuffer[0] := 'A'; ChrBuffer[1] := 'B'; ChrBuffer[2] := 'C'; // Перехват ошибок в случае, если файл не может быть создан try // Создать экземпляр потока файла, записать в него данные и уничтожить его, чтобы предотвратить утечки памяти fsOut := TFileStream.Create( C_FNAME, fmCreate); fsOut.Write(ChrBuffer, sizeof(ChrBuffer)); fsOut.Free; // Обработка ошибки except on E:Exception do writeln('Файл ', C_FNAME, ' не создан, так как: ', E.Message); end; // Выводим результат операции writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.'); //Ожидаем нажатие Enter readln; end.
Вы так же можете загрузить весь файл в память, если его размер существенно меньше, чем имеющийся системной памяти. Если файл превышает размер системной памяти, то ваша операционная система начнет использовать файл подкачки, снижая скорость работы с файлом.
program ReadBinaryDataInMemoryForAppend; {$mode objfpc} uses Classes, Sysutils; const C_FNAME = 'binarydata.bin'; var msApp: TMemoryStream; begin // Создаем поток в памяти msApp := TMemoryStream.Create; // Перехват ошибок в случае их возникновения try // Считать данные в память msApp.LoadFromFile(C_FNAME); // Переходим в конец данных msApp.Seek(0, soEnd); // Запись неких данных в поток msApp.WriteByte(68); msApp.WriteAnsiString('Некий текст'); msApp.WriteDWord(671202); // Запись данных на диск (файл будет перезаписан) msApp.SaveToFile(C_FNAME); // Обработка ошибки except on E:Exception do writeln('Файл ', C_FNAME, ' не удалось считать или записать, так как: ', E.Message); end; // Освобождаем память и уничтожаем объект msApp.Free; // Выводим результат операции и ожидаем нажатие Enter writeln('Файл ', C_FNAME, ' дописан. Нажмите ВВОД для выхода.'); readln; end.
Для работы с файлами большого объёма, рекомендуется использовать буфер, например в 4096 байт.
var TotalBytesRead, BytesRead : Int64; Buffer : array [0..4095] of byte; // или, array [0..4095] of char FileStream : TFileStream; try FileStream := TFileStream.Create; FileStream.Position := 0; // Установим позицию в начало файла while TotalBytesRead <= FileStream.Size do // Пока объем считанных данных меньше размера файла begin BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // Считать 4096 байт данных inc(TotalBytesRead, BytesRead); // Увеличиваем TotalByteRead на размер буфера, т.е. 4096 байт // Что-то делаем с буфером данных end;
Копирование файла
Теперь,зная методы работы с файлами, мы можем реализовать простую функцию копирования файла, скажем FileCopy.(такой функции нет в FreePascal, хотя Lazarus её имеет copyfile):
program FileCopyDemo; // Пример функции FileCopy {$mode objfpc} uses classes; const fSource = 'test.txt'; fTarget = 'test.bak'; function FileCopy(Source, Target: string): boolean; // Копируем файл с путем в Source в файл с путем Target. // Кэшируем весь файл в память. // В случаи успеха возвращаем true, в случаи ошибки - false. var MemBuffer: TMemoryStream; begin result := false; MemBuffer := TMemoryStream.Create; try MemBuffer.LoadFromFile(Source); MemBuffer.SaveToFile(Target); result := true except //Подавляем исключение; результатом функции является значение false по умолчанию end; // Очистка MemBuffer.Free end; // Пример использования begin If FileCopy(fSource, fTarget) then writeln('Файл ', fSource, ' скопирован в ', ftarget) else writeln('Файл ', fSource, ' не скопирован в ', ftarget); readln() end.
Обработка текстовых файлов (TStringList)
Для текстовых файлов можно использовать класс TStringList, чтобы загрузить весь файл в память и иметь легкий доступ к его строкам. Вы также можете записать StringList обратно в файл:
program StringListDemo; {$mode objfpc} uses Classes, SysUtils; const C_FNAME = 'textfile.txt'; var slInfo: TStringList; begin // Создаем TStringList для обработки текстового файла slInfo := TStringList.Create; // Для обработки исключений, используем блок try/except try // Загружаем файл в память slInfo.LoadFromFile(C_FNAME); // Добавляем некие строки slInfo.Add('Некая строка'); slInfo.Add('Ещё одна строка.'); slInfo.Add('Всё, хватит.'); slInfo.Add('Сейчас ' + DateTimeToStr(now)); // Записать содержимое на диск, заменив исходное содержимое slInfo.SaveToFile(C_FNAME); except // Обработка ошибки on E: EInOutError do writeln('Произошла ошибка обработки файла. Причина: ', E.Message); end; // Очистка slInfo.Free; // Выводим результат операции и ожидаем нажатие Enter writeln('Файл ', C_FNAME, ' обновлен. Нажмите ВВОД для выхода.'); readln; end.
Демо: сохранить одну строку в файл
Для того, чтобы сохранить одну строку в файл, вы можете воспользоваться процедурой, описанной ниже.
program SaveStringToPathDemo; {$mode objfpc} uses Classes, sysutils; const C_FNAME = 'textstringtofile.txt'; // SaveStringToFile: функция для хранения строк текста в файле на диске. // Если результат функции равен True, то строка была написана // Иначе произошла ошибка function SaveStringToFile(theString, filePath: AnsiString): boolean; var fsOut: TFileStream; begin // По умолчанию результат неудачный result := false; // Записать данную строку в файл, перехватывая ошибки в процессе записи. try fsOut := TFileStream.Create(filePath, fmCreate); fsOut.Write(theString[1], length(theString)); fsOut.Free; // На данном этапе известно, что запись прошла успешно. result := true except on E:Exception do writeln('Строка не записана. Детали: ', E.ClassName, ': ', E.Message); end end; // // Основная программа // begin // Пытаемся сохранить текст в файл и выводим результат операции if SaveStringToFile('>> этот текст сохраняется <<', C_FNAME) then writeln('Текст успешно записан в файл.') else writeln('Не удалось сохранить текст в файл.'); // Ждем нажатия Enter readln end.
Смотрите так же
- CopyFile — функция Lazarus, которая копирует файл
- File
Дополнительные функции работы с файлами
Модуль |
|
ChDir |
Выполняет |
MkDir |
Создает |
RmDir |
Удаляет |
GetDir |
Получить |
Модуль |
|
DiskFree |
Число |
DiskSize |
Размер |
GetFAttr |
Получение |
SetFAttr |
Задание |
FSplit |
Получение |
FExpand |
Формирование |
FSearch |
Поиск |
FindFirst |
Поиск |
FindNext |
Поиск |
Прототипы
процедур FindFirst
и FindNext
имеют вид:
FindFirst(Path:
String; Attrib: Word; Var SR: SearchRec);
FindNext(Var
SR:
SearchRec);
Для
работы с этими подпрограммами требуются
следующие предопределенные описания:
-
Константы
Const
ReadOnly
= $01; только
для чтения
Hidden = $02; скрытый
SysFile =
$04; системный (непереносимый)
Volume ID = $08; метка
диска
Directory =
$10; подкаталог
Archive = $20; архивный
AnyFile = $3F; сумма
всех предыдущих
Эти атрибуты можно
складывать или вычитать (из anyfile).
-
Переменная
DOSError.
Она используется для анализа ошибок
MS
DOS.
Значение этой переменной, равное нулю,
соответствует отсутствию ошибки. Смысл
некоторых ненулевых кодов следующий:
2
– файл не найден
3
– маршрут не найден
5
– доступ к файлу запрещен
6
– неправильная обработка
8
– недостаточно памяти
10
– неверные установки значений параметров
среды
11
– неправильный формат
18
– файлов нет
При
работе процедуры FindFirst
возможны ошибки с номерами 2 и 18, а при
работе FindNext
– только 18.
3)
Тип
Type
SearchRec
= record
Fill: array[1..21] of byte; {системное поле}
attr: byte; {байт атрибутов}
time: longint; {время
создания}
size: longint; {размер
файла}
name: string[12]; {имя файла}
end;
Поля
переменной этого типа содержат информацию
о последнем файле, найденном с помощью
FindFirst
и FindNext.
Процедура
FindFirst
при заданных имени файла и атрибутах
должна вызываться лишь один раз. Она
записывает в поля переданной ей переменной
SR
типа SearchRec
информацию о первом найденном файле,
удовлетворяющем заданным условиям. Эта
информация в дальнейшем будет
использоваться процедурой FindNext,
которая всегда вызывается после FindFirst
и заполняет поля переменной SR
информацией о следующем найденном
файле.
Контроль
работы этих процедур ведется с помощью
переменной DOSError:
если файла нет, то DOSError<>0.
Обработка ошибок ввода-вывода
Все рассмотренные
ранее программы были написаны так, что
возможные ошибки ввода-вывода
игнорировались. Однако реально это не
так. В ТР стандартной реакцией на наличие
ошибок в вашей программе является
следующее: ваша программа аварийно
завершается и выдает сообщение:
Runtime
error
<номер> <смещение>.
Такое сообщение
буде полезно вам как программистам при
поиске места и причины ошибки в программе.
Однако показывать такое сообщение
пользователю вашей программы будет не
совсем дружественно.
Для того, чтобы
ваша программа из-за ошибки аварийно
не завершалась и для того, чтобы не
пугать пользователей непонятными им
сообщениями, вам необходимо перехватить
инициативу по обработке ошибок у системы
и писать собственные обработчики ошибок
стадии выполнения (исключений). Традиционно
обработка ошибок в программах
осуществляется с помощью установки
флагов (в
случае ошибки) и последующего анализа
этих флагов. Для перехвата ошибок
ввода-вывода на Паскале необходимо:
-
Специальным
образом оформить каждый фрагмент
программы, которые потенциально могут
вызвать ошибку. К таким фрагментам
относятся — открытие файда, чтение из
файла, запись в файл. -
При наличии такого
специального оформления необходимо
по завершению фрагмента проверить:
была ошибка или нет. Если была, то вы
должны выполнить ее обработку (хотя бы
вывести свое сообщение).
Пример:
reset(f);
при попытки открытия
файла физически файл может отсутствовать
на диске. Этот оператор в программе
является потенциальным источником
ошибки.
Специальное
оформление потенциально опасных участков
программы состоит в том, что
вы должны
как бы завернуть каждый опасный участок
в специальный код: выше
и ниже опасного фрагмента программы
вы должны
расположить две директивы компилятора:
{$I-}
— указывает на то, что необходимо
отключить системный контроль ошибок
reset(f);
{$I+}
— указывает на то, что необходимо включить
системный контроль ошибок
При наличии
директивы компилятора {$I-}
даже при наличии ошибок в/в программа
аварийно не завершается и ход выполнения
программы не нарушается.
Замечание:
По умолчанию действует директива {$i+}.
Контроль ошибок
в/в производится с помощью функции
IOResult
. Если ошибка имеет место, то значение
этой функции отлично от нуля. Поэтому
типичный шаблон обработки ошибки
ввода-вывода имеет вид:
If
IOResult
<> 0 Then
{обработка ошибки};
Обычно обработка
ошибки сводится к сообщению об ошибки
и завершению вашей программы (с помощью
процедуры HALT
или EXIT).
Замечание:
Опросить функцию IOResult
можно лишь
один раз
после каждой операции ввода-вывода,
т.к. она обнуляет свое значение перед
каждым вызовом. Поэтому целесообразно
сохранять значение, возвращенное
IOResult,
в специально выделенной переменной.
Пример.
{$I-}
reset(f);
{$I+}
If
IOResult <> 0
then
begin
Writeln(‘Ошибка
открытия
файла’);
Halt;
end;
Соседние файлы в папке WORD
- #
15.04.2015439.06 Кб306.docx
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Компилятор Турбо Паскаля позволяет генерировать выполнимый код в двух режимах: с проверкой корректности ввода-вывода и без нее. В среде программирования этот режим включается в меню
— 262 —
Options/Compiler/IO-checking. При включении в программу ключ режима компиляции обозначается как
{$I+} — режим проверки включен;
{$I-} — режим отключен.
По умолчанию, как правило, действует режим $I+. Этот ключ компиляции имеет локальную сферу влияния. Можно многократно включать и выключать режим, вставляя в текст программы конструкции {$I+} и {SI-}, тем самым создавая области с контролем ввода-вывода и без него.
При включенном режиме проверки любая ошибка ввода-вывода будет фатальной: программа прервется, выдав номер ошибки. Возможные номера ошибок ввода-вывода находятся в диапазоне от 2 до 200 ( от 2 до 99 — это коды ошибок DOS, от 100 до 149 — ошибки, диагностируемые самой программой, и от 150 до 200 — критические аппаратные ошибки). Расшифровка кодов ошибок с краткими комментариями приведена в табл. 12.6 в конце этого раздела.
Если отключить режим проверки, то при возникновении ошибки ввода-вывода программа уже не будет прерываться, а продолжит работу со следующего оператора. Результат операции ввода-вывода, вызвавшей ошибку, будет неопределен. При этом код ошибки будет сохранен в предопределенной системной переменной InOutRes. Однако для опроса этого кода лучше пользоваться специальной функцией Турбо Паскаля.
7.4 Обработка текстовых файлов в языке Free Pascal
При работе с текстовыми файлами следует учесть следующее:
- Действие процедур reset, rewrite, close, rename, erase и функции eof аналогично их действию при работе с компонентными (типизированными) файлами.
- Процедуры seek, trunсate и функция filepos не работают с текстовыми файлами.
- Можно пользоваться процедурой открытия текстового файла append(f), где f — имя файловой переменной. Эта процедура служит для открытия файла в режиме дозаписи в конец файла. Она применима только к уже физически существующим файлам, открывает и готовит их для добавления информации в конец файла.
- Запись и чтение в текстовый файл осуществляются с помощью процедур write, writeln, read, readln следующей структуры:
read ( f, x1, x2, x3,…, xn );
read ( f, x );
readln ( f, x1, x2, x3,…, xn );
readln ( f, x );
write ( f, x1, x2, x3,…, xn );
write ( f, x );
writeln ( f, x1, x2, x3,…, xn );
writeln ( f, x );
В этих операторах f — файловая переменная. В операторах чтения (read, readln) x, x1, x2, x3,…, xn — переменные, в которые происходит чтение из файла. В операторах записи write, writeln x, x1, x2, x3,…, xn — переменные или константы, информация из которых записывается в файл.
Есть ряд особенностей при работе операторов write, writeln, read, readln с текстовыми файлами. Имена переменных могут быть целого, вещественного, символьного и строкового типа. Перед записью данных в текстовый файл с помощью процедуры write происходит их преобразование в тип string. Действие оператора writeln отличается тем, что после указанных переменных и констант в файл записывается символ «конец строки».
При чтении данных из текстового файла с помощью процедур read, readln происходит преобразование из строкового типа к нужному типу данных. Если преобразование невозможно, то генерируется код ошибки, значение которого можно узнать, обратившись к функции IOResult. Компилятор Free Pascal позволяет генерировать код программы в двух режимах: с проверкой корректности ввода-вывода и без неё.
В программу может быть включен ключ режима компиляции. Кроме того, предусмотрен перевод контроля ошибок ввода-вывода из одного состояния в другое:
- {$I+} — режим проверки ошибок ввода-вывода включён;
- {$I-} — режим проверки ошибок ввода-вывода отключён.
По умолчанию, как правило, действует режим {$I+}. Можно многократно включать и выключать режимы, создавая области с контролем ввода и без него. Все ключи компиляции описаны в приложении.
При включённом режиме проверки любая ошибка ввода-вывода будет фатальной, программа прервётся, выдав номер ошибки.
Если убрать режим проверки, то при возникновении ошибки ввода-вывода программа не будет останавливаться, а продолжит работу со следующего оператора. Результат операции ввода-вывода будет неопределён.
Для опроса кода ошибки лучше пользоваться специальной функцией IOResult, но необходимо помнить, что опросить её можно только один раз после каждой операции ввода или вывода: она обнуляет своё значение при каждом вызове. IOResult возвращает целое число, соответствующее коду последней ошибки ввода-вывода. Если IOResult=0, то при вводе-выводе ошибок не было, иначе IOResult возвращает код ошибки. Некоторые коды ошибок приведены в табл. 7.9.
Код ошибки | Описание |
---|---|
2 | файл не найден |
3 | путь не найден |
4 | слишком много открытых файлов |
5 | отказано в доступе |
12 | неверный режим доступа |
15 | неправильный номер диска |
16 | нельзя удалять текущую директорию |
100 | ошибка при чтении с диска |
101 | ошибка при записи на диск |
102 | не применена процедура AssignFile |
103 | файл не открыт |
104 | файл не открыт для ввода |
105 | файл не открыт для вывода |
106 | неверный номер |
150 | диск защищён от записи |
Рассмотрим несколько практических примеров обработки ошибок ввода-вывода:
- При открытии проверить, существует ли заданный файл и возможно ли чтение данных из него.
assign ( f, ’ abc. dat ’ ); {$I-} reset ( f ); {$I+} if IOResult<>0 then writeln ( ’файл не найден или не читается ’ ) else begin read ( f,... ); close ( f ); end;
- Проверить, является ли вводимое с клавиатуры число целым.
var i : integer; begin {$I-} repeat write ( ’введите целое число i ’ ); readln ( i ); until ( IOResult =0); {$I+} {Этот цикл повторяется до тех пор, пока не будет введено целое число.} end.
При работе с текстовым файлом необходимо помнить специальные правила чтения значений переменных:
- Когда вводятся числовые значения, два числа считаются разделёнными, если между ними есть хотя бы один пробел, или символ табуляции, или символ конца строки.
- При вводе строк начало текущей строки идёт сразу за последним, введённым до этого символом. Вводится количество символов, равное объявленной длине строки. Если при чтении встретился символ «конец строки», то работа с этой строкой заканчивается. Сам символ конца строки является разделителем и в переменную никогда не считывается.
- Процедура readln считывает значения текущей строки файла, курсор переводится в новую строку файла, и дальнейший ввод осуществляется с неё.
В качестве примера работы с текстовыми файлами рассмотрим следующую задачу.
ЗАДАЧА 7.8. В текстовом файле abc.txt находятся матрицы и и их размеры. Найти матрицу , которую дописать в файл abc.txt.
Сначала создадим текстовый файл abc.txt следующей структуры: в первой строке через пробел хранятся размеры матрицы (числа N и M), затем построчно хранятся матрицы A и B.
Рис.
7.14.
Файл abc.txt
На рис. 7.14 приведён пример файла abc.txt, в котором хранятся матрицы A(4,5) и B(4,5).
Текст консольного приложения решения задачи 7.8 с комментариями приведён ниже.
program Project1; {$mode objfpc}{$H+} uses Classes, SysUtils { you can add units after this }; var f : Text; i, j, N,M: word; a, b, c : array [ 1.. 1000, 1.. 1000 ] of real; begin //Связываем файловую переменную f с файлом на диске. AssignFile ( f, ’ abc. t x t ’ ); //Открываем файл в режиме чтения. Reset ( f ); //Считываем из первой строки файла abc.txt значения N и M. Read( f,N,M); //Последовательно считываем элементы матрицы А из файла. for i :=1 to N do for j :=1 to M do read ( f, a [ i, j ] ); //Последовательно считываем элементы матрицы B из файла. for i :=1 to N do for j :=1 to M do read ( f, b [ i, j ] ); //Формируем матрицу C=A+B. for i :=1 to N do for j :=1 to M do c [ i, j ] : = a [ i, j ]+b [ i ] [ j ]; //Закрываем файл f. CloseFile ( f ); //Открываем файл в режиме дозаписи. Append( f ); //Дозапись матрицы C в файл. for i :=1 to N do begin for j :=1 to M do //Дописываем в файл очередной элемент матрицы и пробел в //текстовый файл. write ( f, c [ i, j ] : 1 : 2, ’ ’ ); //По окончании вывода строки матрицы переходим на новую //строку в текстовом файле. writeln ( f ); end; //Закрываем файл. CloseFile ( f ); end.
После работы программы файл abc.txt будет примерно таким, как показано на рис. 7.15.
Рис.
7.15.
Файл abc.txt после дозаписи матрицы C
В этом разделе обсуждаются разнообразные,
важные в практическом отношении, примемы
программирования.
1. Обработка ошибок ввода-вывода
В одном из предыдущих разделов был рассмотрен
фрагмент программы, демонстрирующий контроль за
вводимой информацией и не допускающий ввод
некорректных данных. Повторим здесь этот
фрагмент:
PROGRAM CheckInputData_1;
USES CRT;
VAR
x : Real;
BEGIN
ClrScr;
REPEAT
Write(‘Введите число в интервале от 0 до
100: ‘);
Read(x);
if not((x>=0)and(x<=100))
then writeln(‘Ошибка при вводе
числа!’);
UNTIL (x>=0)and(x<=100);
…
…
END.
Приведенная программа не допускает ввода числа
меньше нуля или больше ста. А что произойдет, если
вместо числа ввести (случайно) какой-либо иной
символ? Скорее всего это приведет к
возникновению фатальной (т.е. ведущий к
прекращению работы программы) ошибки
ввода-вывода: Error 106: Invalid numeric format. Этого можно избежать, если
воспользоваться приемом, описанным ниже.
Сначала немного теории. Компилятор
языка Турбо Паскаль позволяет генерировать
выполнимый (машинный) код в
дух режимах: с проверкой корректности
ввода-вывода и без нее. В среде программирования
этот режим включается в меню Options – Compiler –
I/O Checking. Режимом компиляции
можно управлять непосредственно в тексте
программы с помощью ключа компиляции $I
{$I+} – режим проверки включен;
{$I-} – режим отключен.
По умолчанию, как правило, действует режим $I+. Этот ключ компиляции имеет
локальную сферу влияния. Можно многократно
включать и выключать режим, вставляя в текст
программы конструкции {$I+} и {$I-},
тем самым создавая области с
контролем ввода-вывода и без него.
При включенном режиме проверки любая ошибка
ввода-вывода будет фатальной: программа
прервется, выдав номер ошибки. Если отключить
режим проверки, то при возникновении ошибки
ввода-вывода программа уже не будет прерываться,
а продолжит работу со следующего оператора.
Результат операции ввода-вывода, вызвавший
ошибку, будет неопределен. При этом код ошибки
будет сохранен и в последствии его можно узнать с
помощью специальной функции IOResult. Эта функция возвращает целое число,
соответствующее коду последней ошибки
ввода-вывода. Если же операция ввода-вывода
прошла без сбоев, то функция вернет значение 0. Опросить функцию IOResult можно только один раз, ибо она
обнуляет свое значение при каждом вызове. Обычно
это обходят запоминанием значения функции в
какой-либо переменной.
Итак, для того чтобы рассмотренная выше
программа не давала сбоев при вводе, необходимо
отключить режим проверки ошибок ввода-вывода на время работы
процедуры Read, а затем
проанализировать значение функции IOResult:
PROGRAM CheckInputData_2;
USES CRT;
VAR
x : Real;
err : Integer;
BEGIN
ClrScr;
REPEAT
Write(‘Введите число в интервале от 0 до 100: ‘);
{$I-}
Read(x);
{$I+}
err := IOResult;
if not((x>=0) and (x<=100)) or (err<>0)
then writeln(‘Ошибка при вводе числа!’);
UNTIL (x>=0) and (x<=100) and (err=0);
…
…
END.
Возможность управлять режимом обработки
ошибок и наличие функции IOResult
позволяет писать программы, никогда не дающие
сбоев при вводе или выводе данных и при работе с
файлами.
2. Управление программой с помощью меню
При написании больших и не очень больших
программ, выполняющих различные действия,
полезно сделать так, чтобы у пользователя была
возможность выбрать тот или иной режим работы с
программой. Например, если программа направлена
на решение некоторой физической задачи, то она
может иметь по крайней мере три режима работы: 1)
ввод и модификация параметров задачи; 2)
собственно вычисления; 3) представление
результатов (например, построение графиков
рассчитанных зависимостей). Организация выбора
режимов работы может быть сделана с помощью меню.
Меню, в данном случае, — это перечень команд или
режимов работы, которые можно выбрать тем или
иным способом. В простейшем случае, меню может
быть представлено на экране нумерованным списком. Выбор того или иного пункта
меню осуществляется вводом числа,
соответствующего номеру пункта. В более сложных
случаях выбор пункта меню может осуществляться
перемещением (с помощью клавиатуры или “мыши”)
курсора-подсветки, а активизация выбранного
пункта – нажатием на клавишу “Enter” или щелчком кнопки мыши.
Рассмотрим в качестве примера структуру
программы, содержащей простейшее меню в виде
нумерованного списка.
PROGRAM Example_of_Menu;
USES CRT;
VAR Ch : Char;
PROCEDURE ChangeParam;
BEGIN
END;
PROCEDURE Calculation;
BEGIN
END;
PROCEDURE ShowGraphic;
BEGIN
END;
BEGIN
repeat
ClrScr;
GotoXY(30,2);
Write(‘НАЗВАНИЕ ПРОГРАММЫ’);
GotoXY(28,6);
Write(‘- Меню режимов работы -’);
GotoXY(20,8); Write(‘1 — Ввод и изменение
параметров’);
GotoXY(20,10); Write(‘2 — Вычисление’);
GotoXY(20,12); Write(‘3 — Построение графика’);
GotoXY(20,14); Write(‘4 — Завершение работы’);
GotoXY(15,18);
Write(‘Для выбора режима работы нажмите
клавишу [1..4]: ’);
repeat
Ch := ReadKey;
until Ch in [’1’,’2’,’3’,’4’];
case Ch of
‘1’ : ChangeParam;
‘2’ : Calculation;
’3’ : ShowGraphic;
’4’ : Halt;
end; {case}
until false;
END.
Права на материал принадлежат их авторам
Вернуться на главную страницу