- •Windows -приложение
- •Среда программирования
- •Встроенный отладчик
- •Использование графики
- •Графические данные и палитра
- •Сохранение проекта
- •Структура приложения
- •Структура модуля
- •Простые типы
- •Символьные типы
- •Логические типы
- •Тип перечень
- •Составной оператор
- •Оператор if
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла repeat
- •Пример приложения 6
- •Пример приложения 7
- •Статические массивы
- •Динамические массивы
- •Оператор with
- •Идентичность типов
- •Совместимость типов
- •Преобразование типов
- •Операторы обработки исключительных ситуаций
- •Рекурсия
- •Процедура exit
- •Директивы подпрограммы
- •Класс как объектный тип
- •Наследование
- •Операции is и as
- •Типы ссылки на класс
- •Типизированные файлы
- •Файлы без типа
- •Пример приложения 17
- •Компонент tmainmenii
- •Двунаправленные списки
- •Потоки данных
- •Пример приложения 22
- •Интерфейс drag and drop
- •Пример приложения 24
- •С файлами
- •Пример приложения 26
- •Программные потоки
- •Приоритеты потоков
- •Класс tthread
- •Проблемы синхронизации потоков
Пример приложения 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. С помощью Visible можно скрыть любой пункт меню или подменю. Свойство 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;
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
Рис- 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
Связанный список дает варианты альтернативного представления массивов. Отличие заключается в том, что число элементов в списке заранее неизвестно. Однако на этапе вьшолнения программы памяти под список выделяется ровно столько, сколько требуется для записи его элементов.
Основными операциями, которые выполняются при работе с динамическими структурами данных, являются следующие: добавление элемента, исключение элемента, поиск заданного элемента.