По первому тесту выполнил дважды задания. Результат получается правильный (проверял калькулятором). Пишет, что "Задание не проверено" и предлагает повторить. |
Обработка файлов средствами Free Pascal
7.2.8 Функция filesize
Одной из проблем при копировании данных из типизированного файла в массив является невозможность корректного выделения памяти под массив. При выделении памяти заранее неизвестно, сколько элементов находится в файле. Для решения этой задачи существует функция filesize(f) (f — файловая переменная), которая возвращает значение типа longint — число реальных компонентов в открытом файле, связанном с файловой переменной f. Для пустого файла функция возвращает число 0.
Рассмотрим использование функции filesize при переписывании чисел из файла вещественных чисел в массив.
program Project1; {$mode objfpc}{$H+} uses Classes, SysUtils { you can add units after this }; //Massiw - тип данных - массив из 1000 элементов. type massiw=array [ 1.. 1000 ] of real; var f : file of real; //a - указатель на массив; a :^ massiw; n, i : word; begin //Связываем файловую переменную с реальным файлом на диске. assign file ( f, ’ /home/ evgeniy/pascal /6/ pr1 / abc. dat ’ ); //Открываем файл для чтения. reset ( f ); //Записываем в переменную n количество элементов в файле f. n:= file size ( f ); writeln ( ’в файле ’, n, ’ чисел ’ ); //Выделяем память для динамического массива a //из n вещественных чисел. getmem( a, n * sizeof ( real ) ); for i :=1 to n do begin //Считываем очередной элемент из файла в массив. read ( f, a ^[ i ] ); //Вывод элемента на экран. write ( a ^[ i ] : 1 : 3, ’ ’ ) end; //Освобождаем память, выделенную для динамического массива. freemem ( a, n * sizeof ( real ) ); //Закрываем файл. close file ( f ); readln; end.
7.2.9 Функция filepos
Функция filepos(f) возвращает значение типа longint — текущую позицию в открытом файле, связанном с файловой переменной f. Сразу после открытия файла значение filepos(f) равно 0. После прочтения последнего компонента из файла значение filepos(f) совпадает со значением filesize(f). Достижение конца файла можно проверить с помощью условия:
if filepos ( f )= file size ( f ) then writeln ( ’достигнут конца файла ’ );
7.2.10 Процедура seek
Процедура seek(f,n) устанавливает указатель в открытом файле, связанном с файловой переменной f, на компонент с номером n (нумерация компонентов идет от 0). Затем значение компонента может быть считано.
7.2.11 Процедура truncate
Процедура truncate(f), где f — имя файловой переменной, отсекает часть открытого файла, начиная с текущего компонента, и подтягивает на его место конец файла.
Использование процедур seek и truncate рассмотрим на примере решения следующих двух несложных задач по обработке файлов.
Рассмотрим два варианта решения этой задачи.
В первом, консольном варианте программы после считывания компонентов файла в массив происходит поиск минимального и максимального элементов массива и их индексов. Затем максимальное и минимальное значения перезаписываются в файл.
program Project1; {$mode objfpc}{$H+} uses Classes, SysUtils { you can add units after this }; var f : file of real; i, nmax, nmin : integer; a : array [ 0.. 200 ] of real; max, min : real; begin assignfile ( f, ’ /home/ evgeniy/pascal /6/ pr1 / abc. dat ’ ); reset ( f ); //Cчитываем компоненты файла в массив а. for i :=0 to file size ( f ) -1 do begin read ( f, a [ i ] ); write ( a [ i ] : 1 : 2, ’ ’ ); end; //Начальное присваивание максимального и //минимального элементов массива и их индексов. max:=a [ 0 ]; nmax : = 0; min:=a [ 0 ]; nmin : = 0; //Основной цикл для поиска максимального b //минимального элементов массива и их индексов. for i :=1 to file size ( f ) -1 do begin if a [ i ]>max then begin max:=a [ i ]; nmax:= i end; if a [ i ]<min then begin min:=a [ i ]; nmin:= i end; end; //Перезапись максимального и минимального значений в файл. //Передвигаем указатель файла к максимальному элементу. seek ( f, nmax ); //Записываем на место максимального элемента минимальный. write ( f, min ); //Передвигаем указатель файла к минимального элементу. seek ( f, nmin ); //Записываем на место максимального элемента минимальный. write ( f, max ); //Обязательно закрываем файл. closefile ( f ); readln; end.
Вторую версию программы напишем как полноценное приложение. Использовать массив в программе не будем. Будет организован единственный цикл, в котором очередное значение считывается в переменную a и осуществляется поиск минимального и максимального элементов среди компонентов файла и их индексов. Затем происходит перезапись в файл максимального и минимального значений.
Разработку программы начнём с создания шаблона графического приложения (Проект — Создать Проект — Приложение).
На форме расположим следующие компоненты:
- Две метки Label1 и Label2 для подписи.
- Edit1 — текстовое поле редактирования, в котором будем хранить содержимое исходного файла.
- Edit2 — текстовое поле редактирования, в котором будет храниться файл после преобразования.
- OpenDialog1 — компонент для выбора имени обрабатываемого файла.
- Button1 — кнопка для запуска программы.
- Button2 — кнопка для завершения программы.
Расположим компоненты на форме примерно так, показано на рис. 7.7.
Основные свойства компонентов будем устанавливать программно при создании формы. Поэтому подпрограмма FormCreate будет такой:
procedure TForm1. FormCreate ( Sender : TObject ); begin Form1. Caption := ’Обмен максимального и минимального элемента в файле ’; label1.Caption := ’Исходный файл ’; label1.Visible := False; label2.Caption := ’Файл после преоброзования ’; label2.Visible := False; Edit1.Text := ’ ’; Edit1.Visible := false; Edit2.Text := ’ ’; Edit2.Visible := false; OpenDialog1. Filter := ’Файлы вещественных_чисел | *. datВсе | файлы | *. * ’; OpenDialog1. InitialDir := ’ /home/ evgeniy/pascal /6/ pr1 ’; Button1. Caption := ’Преобразовать файл ’; Button2. Caption := ’Выход ’; Button2. Visible := False; end;
Авторы надеются, что читателям, дочитавшим книгу до этого момента, текст процедуры TForm1.FormCreate понятен без пояснений.
При запуске программы окно формы будет иметь вид, представленный на рис. 7.8.
Основные действия программа будет осуществлять при щелчке по кнопке Преобразовать файл, текст соответствующей подпрограммы с комментариями приведён ниже.
procedure TForm1. Button1Click ( Sender : TObject ); var f : file of real; i, nmax, nmin : integer; a, max, min : real; s1, s : string; begin //Открываем диалоговое окно для выбора файла. if OpenDialog1. Execute then begin //В переменную s записываем полное имя файла. s := OpenDialog1. FileName; //Связываем файловую переменную с файлом на диске. assignfile ( f, s ); //Открываем файл в режиме чтения. reset ( f ); //Делаем видимыми первую метку и поле ввода. label1 Visible := true; Edit1.Visible := true; //В строке s1 будем формировать содержимое файла. S1:= ’ ’; //Цикл для последовательного чтения всех элементов из файла //вещественных чисел. for i :=0 to file size ( f ) -1 do begin //Считывание очередного элемента массива в переменную a. read ( f, a ); if i =0 then //Начальное присваивание максимального и минимального значений //и их индексов. begin max:=a; nmax:= i; min:=a; nmin:= i; end else begin //Сравнение текущего значения с максимальным (минимальным). if max<a then begin max:=a; nmax:= i end; if min>a then begin min:=a; nmin:= i end end; //Добавление очередного значения к выходной строке s1. s1 := s1+FloatToStr ( a)+ ’ ’; end; //Вывод содержимого файла в первое текстовое поле //редактирования. Edit1. Text := s1; //Запрет изменения текстового поля. Edit1. ReadOnly:= true; //Перезапись максимального и минимального значений в файл. //Передвигаем указатель файла к максимальному элементу. seek ( f, nmax ); //Записываем на место максимального элемента минимальный. write ( f, min ); //Передвигаем указатель файла к максимальному элементу. seek ( f, nmin ); //Записываем на место минимального элемента максимальный. write ( f, max ); //Обязательно закрываем файл. closefile ( f ); reset ( f ); //Делаем видимыми вторую метку и поле ввода. label2.Visible := true; Edit2.Visible := true; //Считываем данные из преобразованного файла s1 := ’ ’; for i :=0 to file size ( f ) -1 do begin read ( f, a ); //Добавление очередного значения из преобразованного файла //к выходной строке s1. s1 := s1+FloatToStr ( a)+ ’ ’; end; //Вывод содержимого преобразованного файла во второе текстовое //поле редактирования. Edit2. Text := s1; //Запрет изменения текстового поля. Edit2. ReadOnly:= true; //Делаем видимой вторую кнопку. Button2. Visible :=True; CloseFile ( f ); end; end;
При щелчке по кнопке Выход программа должна завершать свою работу. Поэтому текст обработчика щелчка по кнопке будет очень простым.
procedure TForm_File. Button2Click ( Sender : TObject ); begin Close; end;
При щелчке по кнопке Преобразовать файл появляется окно выбора файла, подобное представленному на рис. 7.5. После выбора файла в нём происходит обмен максимального и минимального элементов и вывод содержимого файла до и после преобразования (см. рис. 7.9). При щелчке по кнопке Выход работа программы завершается.
При решении задачи 7.4 была использована процедура seek, с помощью которой стало возможным изменение данных в файле непосредственно на диске, без считывания в массив.
Рассмотрим две программы, решающие эту задачу. Программы будем создавать как консольные приложения в Lazarus. Алгоритм первой состоит в следующем: считываем компоненты файла в массив, в котором находим максимальный и минимальный элементы и их номера. Открываем файл для записи и вносим в него все элементы, за исключением максимального и минимального.
program Project1; {$mode objfpc}{$H+} uses Classes, SysUtils { you can add units after this }; var f : file of real; max, min : real; j, i, nmax, nmin : integer; a : array [ 1.. 300 ] of real; begin assignfile ( f, ’ /home/ evgeniy/pascal /6/ pr1 / abc. dat ’ ); reset ( f ); //Запоминаем в переменной j количество компонентов в файле. j := filesize ( f ); //Считываем компоненты файла в массив а. for i :=1 to j do read ( f, a [ i ] ); for i :=1 to j do write ( a [ i ] : 1 : 2, ’ ’ ); writeln; closefile ( f ); //Открываем файл для записи. rewrite ( f ); //Начальное присваивание максимального и //минимального элементов массива и их индексов. max:=a [ 1 ]; min:=a [ 1 ]; nmax : = 1; nmin : = 1; //Основной цикл для поиска максимального и //минимального элементов массива и их индексов. for i :=2 to j do begin if a [ i ]>max then begin max:=a [ i ]; nmax:= i end; if a [ i ]<min then begin min:=a [ i ]; nmin:= i; end; end; //Перезапись элементов массива в файл, за исключением //элементов с номерами nmax и nmin. writeln ( ’Преобразованный_файл ’ ); for i :=1 to j do if ( i <>nmax) and ( i <>nmin ) then begin write ( f, a [ i ] ); //Вывод записанного в файл элемента на экран. write ( a [ i ] : 1 : 2, ’ ’ ); end; closefile ( f ); readln; end.
Вторая программа работает следующим образом. Находим максимальный и минимальный элементы и их номера среди компонентов файла. Если nmin > nmax, то меняем содержимое переменных nmin и nmax. Далее элементы, лежащие между минимальным и максимальным (формально между элементами с номерами nmin и nmax), сдвигаем на один порядок влево. Тем самым мы убираем элемент с номером nmin. После этого все компоненты, лежащие после элемента с номером nmax, сдвинем на два порядка влево. Этим мы сотрём максимальный элемент. Затем два последних компонента в файле необходимо удалить.
program Project1; {$mode objfpc}{$H+} uses Classes, SysUtils { you can add units after this }; var f : file of real; a : real; max, min : real; i, nmax, nmin : integer; begin assignfile ( f, ’ /home/ evgeniy/pascal /6/ pr1 / abc. dat ’ ); reset ( f ); //Поиск максимального и минимального элементов в файле и их //индексов. writeln ( ’Исходный файл ’ ); for i :=0 to filesize ( f ) -1 do begin read ( f, a ); write ( a : 1 : 2, ’ ’ ); if i =0 then begin max:=a; nmax:= i; min:=a; nmin:= i end else begin if a>max then begin max:=a; nmax:= i; end; if a<min then begin min:=a; nmin:= i; end end end; writeln; //Сравниваем nmin и nmax. if nmax<nmin then begin i :=nmax; nmax:=nmin; nmin:= i end; //Сдвигаем элементы, лежащие между компонентами //с номерами nmin и nmax, на один влево. for i :=nmin to nmax-2 do begin seek ( f, i +1); read ( f, a ); seek ( f, i ); write ( f, a ); end; //Сдвигаем элементы, лежащие после компонента //с номером nmax, на два влево. for i :=nmax to filesize ( f ) -3 do begin seek ( f, i +1); read ( f, a ); write ( a : 1 : 2 ); seek ( f, i - 1); write ( f, a ); write ( a : 1 : 2 ); end; //Отрезаем последние два компонента. truncate ( f ); closefile ( f ); reset ( f ); //Вывод преобразованного файла. writeln ( ’Преобразованный_файл ’ ); for i :=1 to file s i z e ( f ) do begin read ( f, a ); //Вывод записанного в файл элемента на экран. write ( a : 1 : 2, ’ _ ’ ); end; closefile ( f ); readln; end.
В результате работы программы из файла вещественных чисел удалено наибольшее и наименьшее число.
Кроме типизированных файлов широкое применение при обработке числовых данных получили бестиповые файлы.