ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.01.2021
Просмотров: 318
Скачиваний: 1
Лекция 10
6.4. Простейшие алгоритмы
6.4.1. Поиск
6.4.1.1. Линейный поиск
В массиве A: array[iMin..iMax] of ТИП найти элемент равный B.
Листинг. Алгоритм линейного поиска
i:=iMin;
while (i<iMax) and (A[i]<>B) do i:=i+1;
Цикл while завершит свою работу либо при нахождении элемента равного B, либо при переборе всех элементов массива.
На каждом шаге цикла выполняется две проверки. Для упрощения проверок в конец массива A: array[iMin..iMax+1] of ТИП добавляется барьер - элемент равный B.
Листинг. Алгоритм линейного поиска с барьером
i:=iMin; A[iMax+1]:=B;
while A[i]<>B do i:=i+1;
Завершение работы цикла гарантировано, т.к. элемент B в массиве всегда есть. Ожидаемое число шагов – N/2.
6.4.1.2. Поиск делением пополам
В упорядоченном массиве A: array[iMin..iMax] of ТИП найти элемент равный B.
Листинг. Алгоритм поиска деления пополам
A1:=iMin; A2:=iMax; Ok:=false;
while (A1<=A2) and not Ok do
begin
m:=(A1+A2) div 2;
if A[m]=B then Ok:=true
else
if A[m]<B then A1:=m+1 else A2:=m-1;
end;
Листинг. Упрощенный алгоритм поиска деления пополам
A1:=iMin; A2:=iMax;
while A1<A2 do begin
m:=(A1+A2) div 2;
if A[m]=B then begin
A1:=m; A2:=m;
end
else
if A[m]<B then A1:=m+1 else A2:=m;
end;
Завершение работы цикла гарантировано, т.к. A1<m<A2, A1 – увеличивается, A2 - уменьшается. Ожидаемое число шагов – logN.
6.4.1.3. Поиск в массиве строк
В массиве упорядоченных строк AStr: array[iMin..iMax] of string найти строку St: string.
Воспользуемся поиском деления пополам:
Листинг. Поиск в массиве строк
A1:=iMin; A2:=iMax;
while A1<A2 do
begin
m:=(A1+A2) div 2; i:=0;
while (AStr[m,i]=St[i]) and (St[i]<>#0) do i:=i+1;
if AStr[m,i]<St[i] then A1:=m+1 else A2:=m;
end;
if A2<iMax then begin
i:=0;
while (AStr[A2,i]=St[i]) and (St[i]<>#0) do i:=i+1;
end;
6.4.1.4. Прямой поиск строки
В строке s: string[N] найти строку s0: string[M].
Листинг. Прямой поиск строки
i:=0;
repeat
i:=i+1; j:=1;
while (j<M) and (s[i+j-1]=s0[j]) do j:=j+1;
until (j=M) or (i=N-M);
6.4.2. Сортировка
6.4.2.1. Пузырьковая сортировка
Отсортировать массив A: array[iMin..iMax] of ТИП.
Листинг. Пузурьковая сортировка.
for i:=iMin+1 to iMax do
for j:=iMax downto i do
if A[j-1]<A[j] then
begin
t:=A[j-1]; A[j-1]:=A[j]; A[j]:=t;
end;
6.4.2.2. Шейкерная сортировка
Оптимизация предыдущего алгоритма включает в себя следующее:
-
массив можно считать уже упорядоченным, если на последнем проходе не было ни одной перестановки элементов;
-
сравнение пар элементов можно производить только до места последней перестановки: раз не было перестановок, значит – дальше элементы упорядочены;
-
при прохождении массива слева направо (снизу вверх) поднимается легкий пузырек. Почему бы не двигаться по массиву в обратном направлении – сверху вниз, опуская тяжелый пузырек?
Эти моменты учтены в шейкер-сортировке (от англ. shake – трясти), приведенной в листинге 7.6.
Листинг. Шейкерная сортировка.
procedure SortShaker;
var
j,L,left,right: integer;
tmp : integer;
last : integer; // место последней перестановки
begin
L:=Length(A);
left:=1; right:=L-1; last:=L-1;
repeat
for j:=right downto left do //поднимаются легкие пузырьки
if a[j-1]>a[j] then begin
tmp:=a[j]; a[j]:=a[j-1]; a[j-1]:=tmp;
last:=j
end;
left:=last+1; // запомнили место последней перестановки
for j:=left to right do //опускаются тяжелые пузырьки
if a[j-1]>a[j] then begin
tmp:=a[j]; a[j]:=a[j-1]; a[j-1]:=tmp;
last:=j
end;
right:=last-1; // запомнили место последней перестановки
until left>right;
end; //Shaker
6.5. Файлы
Существует три традиционных способа доступа к информации в файле: файл может быть открыт как текстовый, как типизированный и как нетипизированный. Текстовые файлы могут содержать признак конца строки (символы с кодами #13#10) и признак конца файла (символ с кодом #26). Типизированные файлы допускают запись и чтение данных порциями одинаковой длины. Структура этой порции определяется при объявлении файлового указателя. Нетипизированные файлы используются для быстрой записи и копирования данных.
При работе с любым типом данных необходимо обязательно выполнить 5 действий:
-
описать файловый указатель;
-
связать файловый указатель с именем файла;
-
объявить новый файл или существующий;
-
записывать данные или читать их;
-
закрыть файл.
Ниже в таблице 6.1 приведены процедуры и функции, позволяющие реализовать эти действия для всех типов файлов.
Таблица 6.1. Основные действия с файлами
|
№ |
Назначение |
Текстовые |
Типизирован-ные |
Нетипизирован-ные |
|
1 |
Указатель |
F: textfile |
F: file of ТИП X: ТИП |
F: file |
|
2 |
Связать |
AssignFile(f,name) |
AssignFile(f,name) |
AssignFile(f,name) |
|
3 |
Объявить существующий |
Reset(f), Append(f) |
Reset(f) |
Reset(f,1) |
|
3 |
Объявить новый |
Rewrite(f) |
Rewrite(f) |
Rewrite(f,1) |
|
4 |
Чтение |
Read(f,список) Readln(f,список) |
Read(f,x) |
BlockRead (f, buf, SizeOf(buf), NumRead) |
|
4 |
Запись |
Write(f,список) Writeln(f,список) |
Write(f,x) |
BlockWrite (f,buf,SizeOf(buf), NumWrite) |
|
5 |
Закрыть |
CloseFile(f) |
CloseFile(f) |
CloseFile(f) |
Файловый указатель f – любая переменная файлового типа: текстового, типизированного и нетипизированного.
Перед использованием файловой переменной, она должна быть связана с внешним файлом с помощью процедуры AssignFile. Под внешним файлом понимается файл на диске, но это также может быть устройство, например, клавиатура или дисплей.
Если связь с внешним файлом установлена, файловая переменная должна "быть открыта", чтобы подготовить его для чтения или записи. Существующий файл может открываться с помощью процедуры Reset, которая устанавливает файловый указатель в начало файла. Существующий текстовый файл также может открываться с помощью процедуры Append, которая устанавливает файловый указатель в конец файла. Новый файл может создаваться и открываться процедурой Rewrite. Текстовые файлы, открываемые Reset, предназначены только для чтения, а текстовые файлы открытые Rewrite или Append предназначены только для записи. Типизированные файлы и нетипизированные файлы допускают как чтение, так и запись независимо от того, как они открывались Reset или Rewrite.
Каждый файл – линейная последовательность компонентов, которая имеет тип компонента (или тип записи). Нумерация записей начинается с нуля.
Файлы доступны последовательно. При чтении компонента с помощью процедуры Read и при записи процедурой Write, файловый указатель переходит на следующий компонент. Типизированные файлы и нетипизированные файлы могут быть доступными произвольно с помощью процедуры Seek, которая перемещает файловый указатель на указанный компонент. Функции FilePos и FileSize позволяют определять текущую файловую позицию и размер файла.
Когда программа завершает обработку файла, файл должен быть закрыт процедурой CloseFile. После того, как файл закроется, связанный внешний файл будет скорректирован. Файловая переменная может затем связываться с другим внешним файлом.
Помимо основных процедур, представленных в табл. 6.1, существует достаточно много других процедур и функций, предназначенных для работы с файлами. Некоторые из них представлены в табл. 6.2:
Таблица 6.2. Другие процедуры и функции
|
Процедуры или функции |
Описание |
|
Append (var F: Text) |
Открывает существующий текстовый файл для добавления |
|
AssignFile(var F; FileName: string) |
Назначает имя файла в файловую переменную |
|
BlockRead (var F: File; var Buf; Count:Integer[;var AmtTransferred: Integer]) |
Читает одну или более записей из нетипизированного файла. |
|
BlockWrite (var f: File; var Buf; Count: Integer[;var AmtTransferred: Integer]) |
Пишет одну или более записей в нетипизированный файл. |
|
ChDir (S: string) |
Изменяет текущий директорий. |
|
CloseFile (var F) |
Закрывает открытый файл. |
|
Eof(var F) |
Возвращает статус конца файла |
|
Eoln [(var F: Text) ] |
Возвращает статус конца строки текстового файла. |
|
Erase (var F) |
Удаляет файл. |
|
FilePos(var F) |
Возвращает текущую файловую позицию типизированного или нетипизированного файла |
|
FileSize(var F) |
Возвращает текущий размер файла; не использовать для текстовых файлов. |
|
Flush (var F: Text) |
Сбрасывает буфер выходного текстового файла. |
|
GetDir (D: Byte; var S: string) |
Возвращает текущий директорий указанного логического диска. |
|
IOResult: Integer |
Возвращает целую величину, которая является статусом последней выполненной функции ввода/вывода. |
|
MkDir (S: string) |
Создает поддиректорий. |
|
Read (F , V1 [, V2,...,Vn ] ) |
Читает одну или более величин из файла в одну или более переменных. |
|
Readln ([ var F: Text; ] V1 [, V2, ...,Vn ]) |
Выполняет Read и затем переходит к началу следующей строки в текстовом файле. |
|
Rename (var F; Newname) |
Переименовывает файл. |
|
Reset (var F [: File; RecSize: Word ] ) |
Открывает существующий файл. |
|
Rewrite (var F [: File; RecSize: Word ] ) |
Создает и открывает новый файл. |
|
RmDir (S: string) |
Удаляет пустую папку. |
|
Seek (var F; N: Longint) |
Перемещает текущую позицию типизированного или нетипизированного файла. Не используется для текстовых файлов. |
|
SeekEof [ (var F: Text) ] |
Возвращает статус конца текстового файла. |
|
SeekEoln [ (var F: Text) ] |
Возвращает статус конца строки текстового файла. |
|
SetTextBuf (var F: Text; var Buf [ ; Size: Integer] ) |
Назначает буфер I/O для текстового файла. |
|
Truncate (var F) |
Отсекает типизированный или нетипизированный файл с текущей позиции. |
|
Write (F, V1,...,Vn) |
Пишет одну или более величин в файл. |
|
Writeln ([var F: Text;] P1 [,P2, ...,Pn] ) |
Выполняет Write и затем пишет признак конца строки в текстовый файл. |
По умолчанию, все вызовы стандартных процедур и функций ввода/вывода (I/O) автоматически проверяются на ошибки, и если возникает ошибка, то происходит исключение (или программа прекращает свою работу, если не допускается исключительная обработка). Эта автоматически проверка может включаться и выключаться директивами компилятора {$I+} и {$I-}. Если проверка I/O выключена {$I-}, то при возникновении ошибки I/O исключительная обработка не вызывается и необходимо использовать стандартную функцию IOResult.
{$I-}
Reset(f);
{$I+}
if IoResult<>0 then Rewrite(f);
Необходимо вызвать функцию IOResult, для того чтобы очистить ошибку, даже если IOResult не используется. Иначе в состоянии {$I+} вызов следующей функции I/O потерпит неудачу с предыдущей ошибкой IOResult.
В таблице 6.3 приведены коды ошибок, возникающих при работе с файлами. Эти коды присваиваются свойству errorcode, если действует директива компилятору {$I+}, или возвращаются функцией IoResult, если действует директива компилятору {$I-}.
Таблица 6.3. Коды ошибок ввода/вывода
|
Константа |
Код |
|
|
ERROR_FILE_NOT_FOUND |
2 |
Файл не найден |
|
ERROR_PATH_NOT_FOUND |
3 |
Путь не найден |
|
ERROR_TOO_MANY_OPEN_FILES |
4 |
Слишком много открытых файлов |
|
ERROR_ACCESS_DENIED |
5 |
Access is denied |
|
ERROR_NOT_SAME_DEVICE |
17 |
The system cannot move the file to a different disk drive |
|
ERROR_NO_MORE_FILES |
18 |
There are no more files |
|
ERROR_SHARING_VIOLATION |
32 |
The process cannot access the file because it is being used by another process |
|
ERROR_LOCK_VIOLATION |
33 |
The process cannot access the file because another process has locked a portion of the file |
|
ERROR_SHARING_BUFFER_EXCEEDED |
36 |
Too many files opened for sharing |
|
ERROR_HANDLE_EOF |
38 |
Reached the end of the file |
|
ERROR_NO_SPOOL_SPACE |
62 |
Space to store the file waiting to be printed is not available on the server |
|
ERROR_PRINT_CANCELLED |
63 |
Your file waiting to be printed was deleted |
|
ERROR_FILE_EXISTS |
80 |
The file exists |
|
ERROR_CANNOT_MAKE |
82 |
The directory or file cannot be created |
|
ERROR_OPEN_FAILED |
110 |
The system cannot open the device or file specified |
|
ERROR_BUFFER_OVERFLOW |
111 |
The file name is too long |
|
ERROR_DISK_FULL |
112 |
There is not enough space on the disk |
|
ERROR_NO_MORE_SEARCH_HANDLES |
113 |
No more internal file identifiers available |
|
ERROR_INVALID_TARGET_HANDLE |
114 |
The target internal file identifier is incorrect |
|
ERROR_INVALID_NAME |
123 |
The filename, directory name, or volume label syntax is incorrect |
|
ERROR_NEGATIVE_SEEK |
131 |
An attempt was made to move the file pointer before the beginning of the file |
|
ERROR_SEEK_ON_DEVICE |
132 |
The file pointer cannot be set on the specified device or file |
Контроль ошибок ввода/вывода необходим при создании приложений, работающих в локальной сети, когда необходимо обеспечить корректный доступ к одному файлу нескольких пользователей. При этом возникает проблема создания очереди, т.к. одновременный доступ к файлу нескольких приложений невозможен. Наиболее простой способ заключается в использовании цикла repeat:
{$I-}
repeat
Reset(f);
Until IoResult=0;
{$I+}
Этот цикл будет работать до тех пор, пока файл не освободится. Мы будем использовать эту технологию в примере, рассмотренном в пункте 6.5.5.
6.5.1. Текстовые файлы
Если текстовый файл открыт с файловым указателем типа FileText, внешний файл интерпретируется специальным образом: считается, что он представляет последовательность символов сформатированную в строки, где каждая строка заканчивается признаком конца строки (символ возврата каретки, возможно сопровождаемый символом перевода строки #13#10). Тип FileText в каком-то смысле соответствует типу file of Char.
Для текстовых файлов, есть специальные процедуры Read и Write, которые позволяют читать и записывать величины неcимвольного типа. Такие величины автоматически интерпретируются из их символьного представления. Например, Read(F,I), где I – переменная целого типа, читает последовательность цифр, интерпретирует эту последовательность как десятичное целое, и загружает это значение в переменную I.
Есть две стандартные текстфайловые переменные: Input и Output. Стандартная файловая переменная Input указывает на файл только для чтения, связываемый с операционным системным вводом (обычно клавиатурой). Стандартная файловая переменная Output указывает на файл только для записи, связываемый с операционным системным стандартным выводом (обычно дисплеем). Прежде, чем приложение начнет выполняться, файлы Input и Output автоматически открываются, как при выполнении следующих команд:
AssignFile(Input, '');
Reset(Input);
AssignFile(Output, '');
Rewrite(Output);
Примечание:
Текстовый ввод/вывод доступен только в консольных приложениях.
Некоторым стандартным процедурам I/O, работающим с текстовыми файлами, не нужно иметь файловую переменную, явно указанную как параметр. Если файловый параметр опускается, то по умолчанию принимаются Input или Output. Например, Read(X) соответствует Read(Input, X) и Write(X) соответствует Write(Output, X).
Принтер тоже может выступать как текстовый файл для записи. В этом случае вместо процедуры AssignFile(f,FileName) необходимо использовать AssignPrn. Например,
AssignPrn(f); Rewrite(f);
Writeln(f, 'Напечатана одна строка');
CloseFile(f);
Любой принтер управляется с помощью кодов символов и команд, отправляемых на принтер процедурой Write. Первые 32 кода таблицы кодов ASCII называются управляющими кодами. Так как этого количества не хватает для всех команд, то многие команды состоят из двух и более кодов. В этом случае последовательность управляющих кодов для принтера всегда начинается с кода 27. В системе ASCII для этого кода используется аббревиатура ESC, а сами команды называются ESC-последовательностями. Система команд зависит от типа принтера, но во многом эти команды совпадают. Ниже приводится система некоторых команд для принтера Epson FX-800.
Таблица 6.4. Команды для принтера Epson FX-800
|
Обозначения |
Код |
Назначение |
|
BEL |
7 |
Звуковой сигнал на 1 с. |
|
HT |
9 |
Переход в следующую позицию горизонтальной табуляции. Метки горизонтальной табуляции устанавливаются командой Esc D |
|
LF |
10 |
Печатает данные, оставшиеся в буфере и передвигает бумагу на 1 строку. Пропуск строки (1/6 дюйма=4.23 см) может быть изменен командами Esc A, Esc 0, Esc 1, Esc 2, Esc 3 |
|
CR |
13 |
Оканчивает строку принтера и печатает данные из буфера без перевода каретки |
|
SO |
14 |
Изменяет режим печати на двойную ширину |
|
SI |
15 |
Переводит принтер в режим уплотненной печати |
|
Esc - 1 |
27 45 1 |
Печать с подчеркиванием |
|
Esc - 0 |
27 45 0 |
Печать с подчеркиванием отменить |
|
Esc 0 |
27 48 |
Интервал 1/8 дюйма=3.175 мм |
|
Esc 1 |
27 49 |
Интервал 7/72 дюйма=2.74 мм |
|
Esc 2 |
27 50 |
Интервал 1/6 дюйма=4.23 мм |