Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник Емельянов.doc
Скачиваний:
12
Добавлен:
03.11.2018
Размер:
3.25 Mб
Скачать

Пример приложения 17

Пусть имеется некоторый текст, его необходимо занести в текстовый файл, зашифровать, переписывая в другой файл, и таким образом подгото­вить для отправки, например, по электронной почте.

Пусть шифрование осуществляется методом простой одноалфавитной подстановки. Все символы исходного файла заменяются кодами, отличаю­щимися от исходных символов на определенную величину, которая меняет­ся от символа к символу. Для кодирования с помощью генератора случай­ных чисел создается кодировочная таблица, например, из 23 чисел (адресат знает о числе 23). Эта таблица представляет собой алфавит кодирования. Текст разбивается на группы символов, включая #13 #10, и каждый символ изменяется в соответствии с кодировочной таблицей, т.е. код первого сим­вола увеличивается на первое число из таблицы, код второго - на второе число из таблицы и т.д. Для осуществления дешифровки кодировочную таб­лицу также необходимо записать в итоговый файл.

Компонент tmainmenii

При решении данной задачи построим меню с помощью компонента TMainMenu. Это меню можно представить в виде табл. 13. Список объектов меню (строк меню в табл. 13) TMainMenu содержится в свойстве Items. Строки меню имеют тип TMenuItems. TMenu1tems в свою очередь содержит свойство Items, с помощью которого можно создавать подменю. Текст стро­ки меню записывается в свойстве Caption свойства Items.

Таблица 13

Исходный файл

Кодирование

Проверка

Выход

Новый файл

Таблица кодировки

Добавить

Кодирование файла

Прочитать

С активизацией меню связано событие OnClick. Данное событие выпол­няется, когда пользователь выбирает какой-либо пункт меню. К каждому пункту меню необходимо привязать свой обработчик. Доступ к пунктам ме­ню осуществляется по свойству Name (имя). В табл. 14 приводятся варианты имен пунктов меню, которые используются далее в программе.

Таблица 14

MainFile

CodeFile

InfoCompare

ExitPoint

NewFile

CodeTabl

AddFile

InfoCode

ReadFile

Для придания меню более привлекательного вида можно добавлять раз­делительные полосы (достаточно задать одним символом "минус" ("-")). При проектировании меню с помощью редактора меню (рис. 37) необходимо вы­брать имена (свойство Name) пунктам меню (см. табл. 14).

Рис. 37

В пункте меню можно назначить быструю клавишу, для этого перед ка­ким-либо символом нужно поставить символ &, например &Close, и можно вызывать пункт меню в данном случае с помощью Alt + С (первая буква слова Close). Пункту меню можно назначить горячую клавишу, тогда в лю­бое время можно выполнить данный пункт меню. Для этого нужно восполь­зоваться свойством Shortcut. Пункт меню можно сделать недоступным, если в процессе выполнения свойству Enabled задать false. С помощью Visi­ble можно скрыть любой пункт меню или подменю. Свойство Checked по­зволяет отмечать флажком последний выбранный пункт меню.


Рис. 38


Форма примера 17 приводится на рис. 38.

142

143

Справа на компоненте Memo1 формы примера 17 находится изображе­ние компонента MainMenu1, которое будет невидимым при выполнении программы. Ниже приводится программный код примера.

unit Priml7;

interface

uses Windows, Messages, SysUtils, Classes,Graphics, Controls,

Forms, Dialogs, StdCtrls, ExtCtrls, Menus, Buttons; type

TMainForm = class(TForm) MainMenu1: TMainMenu; MainFile: TMenuItem; CodeFile: TMenuItem; InfoCompare: TMenuItem; ExitPoint: TMenuItem; NewFile: TMenuItem; AddFile: TMenuItem; ReadFile: TMenuItem; CodeTabl: TMenuItem; InfoCode: TMenuItem; Panel1: TPanel; Memo1: TMemo; BitBtn1: TBitBtn; BitBtn2: TBitBtn;

procedure NewFileClick(Sender: TObject); procedure ExitPointClick(Sender:.TObject); procedure AddFileClick(Sender: TObject); procedure ReadFileClick(Sender: TObject); procedure CodeTablClick(Sender: TObject); procedure InfoCodeClick(Sender: TObject); procedure BitBtn1Click(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure InfoCompareClick(Sender: TObject); end; Const n=25;

stext='Ошибка открытия файла1; Type TTableCode=array [l..n] of char; var MainForm: TMainForm; TableCode:TTableCode; T: TaxtFile; F: File of TTableCode; FNoType: File; implementation {$R *.DFM}

procedure TMainForm.NewFileClick(Sender: TObject); begin

Panel1.Visible:=true; BitBtn2.Visible:=true;

144

Memo1.SetFocus;

end;

procedure TMainForm.ExitPointClick(Sender: TObject);

begin close;

end;

procedure TMainForm. AddFileClick. (Sender: TObject);

begin

Panel1.Visible:=true;

BitBtn2.Visible:=true;

Memo1.Lines.LoadFromFile('Inpfil.txt');

Memo1.SetFocus;

end;

procedure TMainForm.ReadFileClick(Sender: TObject);

var str:string; begin {$I-}

AssignFile(T,'lnpfil.txt');

reset (T);

{$1+}

if lOResult<>O then begin

MessageDlg(sText,mtError,[mbOk],0); exit; end;

Panel1.Visible:=true; BitBtn2.Visible:=true;

while not eof (T) do begin

readln(T,str);

Memo1.Lines.Add(str);

end;

CloseFile(T); Memo1.SetFocus;

end;

procedure TMainForm.CodeTablClick(Sender: TObject);

var i: byte;

str:string; begin

Randomize;

for i:=l to n do

TableCode[i]:=char(Random(256));

Panel1.Visible:=true ;

BitBtn2.Visible:=false; str:='';

for i:=l to n do str:=str+TableCode[i]+' '; Memo1.Lines.Add(str);

end;

procedure TMainForm.InfoCodeClick(Sender: TObject);

var i,res: integer;

Buffer,Buf:TTableCode; str: string; k: byte; begin

AssignFile(F,'Outfil.dat');

rewrite(F);

if lOResult<>O then begin

MessageDlg(sText,mtError,[mbOk],0); exit ; end;

AssignFile(FNoType,'Inpfil.txt1); reset(FNoType,l); {$!+} if lOResult<>O then begin

MessageDlg(sText,mtError,[inbOk],0); CloseFile(F) ; exit; end;

Write(F,TableCode); Panel1.Visible:=true; BitBtn2.Visible:=false; while not eof(FNoType) do begin BlockRead(FNoType,Buffer,n, res); str:='';

for i:=l to n do begin if i<=res then begin

к:=ord(Buffer[i])+ord(TableCode[i]); Buf[i]:=chr(k);

end else Buf[i]:=TableCode[i]; str:=str+Buf[i]+' '; end;

Memo1.Lines.Add(str); Write(F,Buf);

end;

CloseFile(FNoType); CloseFile(F); end;

procedure TMainForm.BitBtn1Click(Sender: TObject) ; begin

Memo1.Lines.Clear; Panel1.Visible:=false; end;

procedure TMainForm.BitBtn2Click (Sender: TObject) ; begin

Memo1.Lines.SaveToFile{'Inpfil.txt'); BitBtn1Click(Sender);

end;

procedure TMainForm.infoCompareClick(Sender: TObject); var

i: integer;

Buf:TTableCode; str:string; к: byte; begin {$I-}

AssignFile(F,'Outfil.dat’);

reset(F);

if lOResult<>O then begin

MessageDlg(sText,mtError,[mbOk],0); exit; end;

Panel1.Visible:=true;

BitBtn2.Visible:=false; str:=’ ’;

seek(F,l);

while not eof(F) do begin read (F,Buf); for i:-l to n do begin

k:=ord(Buf[i])-ord(TableCode[i]); if k=10 then begin Memo1.Lines.Add(str)t str:='';

end else str:=str+chr(k); end; end;

CloseFile(F); end; end.

Следует отметить, что в данной программе происходит управление дос­тупом к Panel1 и кнопке BitBtn2 (отдельно) с помощью свойств visible.

В примере 17 используются два файла: текстовый - inpFil. txt и типи­зированный - outFil. dat. Для работы с этими файлами введены три фай­ловые переменные. Текстовый файл открывается как стандартный текстовый для корректировки и просмотра и как файл без типа, чтобы посимвольно прочитать информацию и закодировать ее. Для кодирования введена пере­менная к типа Byte. Обратить внимание, что при сложении, возможно, мо­жет получиться значение, выходящее за пределы диапазона этого типа, т.е. более 255. Однако перекодирование происходит корректно, так как при вы­читании также происходит выход за пределы диапазона и прежнее число восстанавливается (полезно поэксперементировать с подобными способами сложения и вычитания, используя отдельную программу). При добавлении

146

147

или занесении новой информации через компонент Memo необходимо в конце текста обязательно набирать Enter.

УКАЗАТЕЛИ

Как было отмечено выше, переменные могут располагаться или в стати­ческой или в динамической памяти. В первом случае под них выделяется вполне определенный объем памяти и связи между программными элемен­тами устанавливаются на этапе компиляции и компоновки программы. Во втором случае память выделяется на этапе выполнения программы. Причем она может освобождаться и повторно выделяться. Это позволяет эффективно ее использовать. При этом используется динамическая память - специаль­но выделенная область оперативной памяти - Heap (куча).

К динамическим переменным относятся рассмотренные выше строки и динамические массивы, а также классы. Переменные данного типа называ­ются ссылками. Работа со ссылками, в общем случае, отличается от работы с обычными переменными тем, что необходимо выделять память на каком-то этапе выполнения программы, а затем освобождать ее. Для строк память выделяется автоматически, автоматически строки (почти все типы) и унич­тожаются. Это достигается довольно сложной структурой строки (кроме па­мяти под значение строки выделяется несколько байт для служебных целей). При работе с динамическими массивами приходится иногда (например, при присваивании) учитывать, что это ссылки. Для создания в программе собст­венных динамических переменных введен в язык Object Pascal тип указа­тель. Указателем иногда называют любую динамическую переменную.

Существуют стандартные указатели Pointer и типизированные указатели. Переменная-указатель - это переменная, которая хранит не сами данные, а адрес размещения этих данных. Указатели Pointer - это просто адреса без указания, что по этим адресам записано. Типизированные указатели со­держат в себе еще информацию о типе хранимых данных.

При объявлении типизированного указателя задается базовый тип дан­ных. В этом случае компилятор легко определяет необходимый объем дина­мической памяти, который требуется выделить данному указателю. Объяв­ление типизированного указателя записывается, например, следующим об­разом:

Type Mas=array [1..10] of real;

PtrMas=^Mas ; Var P:PtrMas;.

148

Это же МОЖНО записать ПО-Другому: Var P:^array [1. .10] of real; . Здесь объявлен указатель Р, который является физическим носителем адреса расположения одномерного массива из 10 вещественных чисел.

В соответствии с объявлением указателя выделяется всего 4 байта стати­ческой памяти для записи адреса. Для записи данных вначале необходимо выделить динамическую память, ее адрес записать в переменную-указатель и после этого размещать данные. Рассмотрим, как синтаксически это может быть записано. Объявим две переменные: Var Pl:^Integer; D:Integer;. Теперь запишем: New{Pl); Р1^:=5; D:=2+P1^; Dispoae(P1) ;. Здесь первый оператор выделяет память, второй записывает число 5. Третий вы­полняет сложение с содержимым выделенной динамической памяти. Чет­вертый освобождает занятую память.

Указателю можно присваивать значение nil - пустой адрес, например P1:=nil;.

Для задания значения указателю можно воспользоваться операцией взя­тия адреса, например P1:=@D;. В данном случае показано, что указателю можно присваивать адрес в статической памяти.

Стандартный указатель объявляется, например, так: var pp: Pointer;. Память под этот указатель выделяется с помощью следующей процедуры: GetMem (РР, 20) ;. Здесь выделено 20 байт динамической памяти. Эту про­цедуру можно использовать и для выделения памяти под типизированные указатели, например, для объявленного выше указателя Р: GetMem (P, SizeOf (Mas)) ;. Функция sizeOf (T) используется для указания размера базового типа для переменной-указателя, здесь т - базовый тип данных. Ес­ли память выделена процедурой GetMem(<указатель>, Size), то для ос­вобождения памяти используется процедура FreeMem (<указатель>, Size). Например, FreeMem(p, SizeOf (Mas)) ;.

К указателям применимы две операции сравнения: "равно" и "не равно". Возможно присваивание их друг другу, например, если объявлено: Var Pl, Р2: ^double; РЗ: Pointer;, то возможны присваивания: Р2:=Р1; или РЗ :=Р2;. Нельзя присваивать Р2 :=РЗ, так как переменная Р2 требует зада­ния базового типа, а РЗ его не имеет. Присваивание РЗ: =Р2 происходит с потерей информации о базовом типе.

Для проверки значения указателя на неравенство nil можно использо­вать функцию Assigned (<указатель>). Эта функция возвращает true, если значение указателя не равно nil. В модулях System и SysUtils размещены различные стандартные подпрограммы работы с указателями и динамиче­ской памятью.

Рассмотрим, каков синтаксис присваивания значений элементам массива Р, объявленного выше: New (р) ,- ра[1]:=6.7; ра[2] :=-3.5; ит.д.

По окончании работы с массивом занятую память нужно освободить: Dispose (P) ;.

149

ПРИМЕР ПРИЛОЖЕНИЯ 18

Прочитать целые числа из текстового файла в память и вывести их в об­ратной последовательности в три столбца: в первом столбце числа со значе­ниями не больше 100, во втором - значения от 100 до 200 включительно, в третьем - значения больше 300. Файл можно подготовить с помощью при­мера 17. Пусть подготовлен файл InpFil.txt. Количество чисел в файле неиз­вестно. Пусть числа типа Byte.

При решении задачи вначале определим возможное количество чисел, чтобы задать объем выделяемой динамической памяти, а затем, предвари­тельно разместив числа в памяти, выведем их на экран дисплея в обратной последовательности. Примем для простоты (и с запасом), что каждое число занимает два байта вместе с разделителем. Форма и вариант расчета приво­дятся на рис. 39.

Рис. 39

В данном примере использовался компонент TListView (страница Win32). Этот элемент управления отображает список в удобном для пользо­вателя виде. В этом списке, кроме текста, могут отображаться пиктограммы. Компоненты списка представляют собой коллекцию элементов типа TListItem. Для добавления их в список используется свойство Items и метод Add. В зависимости от значения свойства ViewStyle список может отобра­жаться различными способами. В примере выбран стиль отображения vsRe-port. TListItem имеет свойство Caption, с помощью которого формируется текст самого левого столбца, и SubItems, с помощью которого формируется текст остальных столбцов. Но прежде чем создавать текст из нескольких столбцов, необходимо последние создать. Столбцы именуются обычно ListColumn, их тип - TListCoIumn. Столбцам можно задать заголовок посред­ством свойства Caption и ширину, используя свойство Width. Кроме того, свойству TListItem-Selected необходимо задать значение true. Порядок ра­боты со всеми этими свойствами приводится в программе.

unit priml8; interface

150

uses Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ComCtrls, ExtCtrls;

type

TForm1 = class(TForm) ListView1: TListView; Panel1: TPanel; Button1: TButton; BitBtn1: TBitBtn; procedure Button1Click(Sender: TObject);

end;

var Porml: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject); type IntArray = array [0..1000] of byte; const stext='Ошибка открытия файла'; var n:integer; p:pointer; a,I,k:integer; s:array [1..3] of string; t:textfile; frfile of char;

ListItem:TListItem; . .

ListColumn:TListCoIumn; begin

{$1-}

AssignFile(f,’npfil.txt');

reset(f);

{$1+}

if LOResult<>O then begin

MessageDlg(sText,mtError,[mbOk],0);

exit; end;

n:=(FileSize(f)+l) div 2; :

CloseFile(f) ; p:=nil; try

GetMem(p,n) ;

{$1-}

AssignFile(t,'Inpfil.txt') ;

reset(t);

{$1+}

if LOResult<>O then begin

MessageDlg(sText,mtError,[mbOk], 0);

Exit;

end;

a:l;

while not eof(t) do begin

151

inc(a);

read(t,IntArray(р^)[a]); end;

CloseFile(t); for i:=l to 3 do begin

ListColumn:=ListView1.Columns.Add; case i of

1: ListColumn.Caption:=' Числа<=100' ;

2: ListColumn.Caption:='100< Числа<=200';

3: ListColumn.Caption:=' Числа>200';

End;

ListColumn.width:=14*Font.Size;

S[1]:='’;

end;

for i:=a-l downto 0 do begin k:= IntArray(p^) [i]

if k<=100 then s[1] :=s[1] + ‘ '+IntToStr (k) else if k<=200 then s[2]:=s[2]+’'+IntToStr(k)

else s[3]:=s[3] + ' '+IntToStr(k);

if dength(s[1])>14) or (length (s [2]) >14) or (length(s[3])>14) or (i=0) then begin ListItem:=ListView1.Items.Add; ListItem.Caption:=s[1]; ListItem.Selected:=true; ListView1.Selected.Subitems.Add(s[2]);

ListView1.Selected.SubItems.Add(s[3]};

s[1]:=’’;

s[2]:=’’;

s[3]:=’’;

ДИНАМИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ

Рассмотрим построение программы, которая имеет дело с людьми, слу­жащими в некоторой фирме. Структура данных для служащего может вклю­чать, например, имя, возраст, подразделение, оклад. Определим структуру данных для менеджера. Во-первых, менеджер является служащим, поэтому к нему относится рассматриваемая структура данных. Во-вторых, к менеджеру относятся некоторые добавочные характеристики, например список служа­щих, которые входят в его группу. В данном случае в памяти необходимо определить служащих, в том числе и менеджера. Но память с данными для менеджера должна содержать еще список подчиненных ему служащих. Оче­видно, этот список должен просто ссылаться в памяти на адреса расположе­ния информации о служащих, чтобы не повторять еще раз упомянутые дан­ные. Таким образом, в данном случае необходимо использовать указатели для построения структур данных {динамических структур данных).

Среди динамических структур данных можно выделить линейные структуры (списки) и структуры-деревья, например, такие:

  • однонаправленные списки;

  • двунаправленные списки;

  • стеки;

  • очереди;

  • бинарные деревья.

Динамические структуры обычно состоят из двух частей. Первая часть включает содержательную информацию, а вторая необходима для построе­ния связей с другими элементами. Эти связи строят с помощью указателей.

end; end; finally

FreeMem(p,n) ; end; end; end.

В элементе ListView1 ширина столбцов связана с размером используе­мого шрифта. Значение 14 для расчета ширины столбца выбрано произволь­но (с помощью оператора IF length(s[1]>14... выводится не более 14 символов в строке). Оператор Try контролирует выделение памяти для ука­зателя р. Обратная последовательность вывода чисел обеспечивается опера­тором Fог ... downto...

152

ОДНОНАПРАВЛЕННЫЕ СПИСКИ

Структура однонаправленного связанного списка приводится на рис. 40, где принято, что содержательная часть состоит из текстовой информации (Info) в виде строки. Звездочка представляет собой указатель, с помощью которого первый элемент связывается со вторым, второй - с третьим и т.д. Конец списка указывается с помощью пустого указателя nil. Построить та­кую структуру можно с помощью следующего объявления:

Туре

PStroka = ^Stroka; Stroka = record

Info:string;

Sled: PStroka; End;.

Здесь вначале объявлен типизированный указатель (pstroka) с базовым типом запись (stroka), а затем сам базовый тип. Звездочка на рис. 40 пред-

153

ставляет указатель Sled, a info - содержательную информацию. Следует обратить внимание, что синтаксически разрешается вначале объявлять ука­затель на несуществующий базовый тип, который обязательно далее должен быть объявлен.

Рис- 40

Построим теперь список, например, из трех элементов. Пусть перемен­ные si, 82, S3 содержат некоторую полезную информацию в виде строк, и пусть объявлены переменные: Var spisok, P:PStroka;. Переменная Spisok будет использована для хранения всего списка, ар- вспомогатель­ный указатель. Напишем такой код для решения задачи:

New(Spisok); {Начало списка}

SpisokA.Info:=S1; {Запись строки SI в список}

New(P) ; {Новый элемент списка}

P^.Info:=S2; {Запись строки S2 в элемент Р}

Spisok^.Sled:=P; {Подключение элемента два в список}

New(P); {Третий элемент списка}

P^.Info:=S3; {Запись строки S3 в элемент Р}

Spisok^.Sled^.Sled:=P; {Подключение элемента 3 в список}

P^.sled:=nil; {Конец списка}

P:=nil; {Теперь указатель Р не нужен}

В соответствии с этим кодом процесс формирования списка можно представить схематично так, как изображено на рис. 41.

Рис. 41

Связанный список дает варианты альтернативного представления масси­вов. Отличие заключается в том, что число элементов в списке заранее неиз­вестно. Однако на этапе вьшолнения программы памяти под список выделя­ется ровно столько, сколько требуется для записи его элементов.

Основными операциями, которые выполняются при работе с динамиче­скими структурами данных, являются следующие: добавление элемента, исключение элемента, поиск заданного элемента.