Программирование на языке Delphi/§6

Материал из Викиверситета

Записи[править]

Наиболее распространенная современная парадигма программирования - это объектно-ориентированное программирование. Объект - в свою очередь есть структура (как правило) разнотипных данных, описывающих некоторую одну сущность или модель. Так, вам может быть нужно описать данные студента с указанием его (1) имени, (2) пола, (3) возраста, и так далее. Логично представить эти данные одним структурным, определенным пользователем типом. Другим примером структуры будет, скажем, объект "треугольник", которые обределяется массивом длин его сторон.

Запись(record) в Delphi - это простейщий структурный тип данных данных, состоящий из фиксированного количества полей (разделов записи), которые могут быть разного типа. 
type
<имя типа> = record
 <список полей> 
end;

Каждый раздел записи состоит из одного или нескольких идентификаторов, которые задают имена полей, за которыми стоит двоеточие с описанием типа данных, к которому принадлежит это поле или поля.

type
  TStudent=record
    FirstName,LastName:string;//имя и фамилия студента
    birthYear:integer;//год рождения
end;

К каждому компоненту записи можно получить доступ, если использовать имя переменной структурного типа, а затем точку и идентификатор поля записи.

type
  TStudent=record
    FirstName,LastName:string;//имя и фамилия студента
    birthYear:integer;//год рождения
end;

var pers:TStudent;
begin
pers.birthYear:=1990;
end.

Для упрощения доступа к полям записи используют оператор with(этот прием удобен, но немного затрудняет отладку, а также чреват неоднозначнотями, когда имена полей разных экзепляров структурных типов, совпадают)

...
with pers do begin
  birthYear:=1990;
  FirstName:='Alexander';
  end;

Множества[править]

Множества -это тип данных, представляющий собой набор взаимосвязанных по какому-либо признаку или группе признаков объектов, которые можно рассматривать как единое целое. Количество элементов, входящих в множество, может менятcя от 0 до 256. Каждый объект в множестве называется элементом множества. Если множество не имеет элементов, то оно называется пустым.

Описание множества имеет тип

<идентификатор> = set of <базовый тип>

Где базовый тип - тип элементов данных. Обычно он задается диапазоном.
Пример задания множества:

var
 dec1:set of 0..9; //Множество задается диапазоном
 dec2:set of byte; //Можно указать тип данных, если его размер - 1 байт

Над множествами определены следующие операции: Пусть M1=[1,2,3,5,8],M2=[1,2,4,6,8]

  • Пересечение множеств(*). Результат содержит элементы общие для обеих множеств(M1*M2=[1,2,8])
  • Объединение множеств(+). Результат содержит элементы первого и второго множеств(M1+M2=[1,2,3,4,5,6,8])
  • Разность множеств(-). Результат содержит элементы первого множества, не входящие во второе(M1-M2=[3,5])
  • Проверка эквивалентности(=). Возвращает True если множества эквивалентны(все элементы совпадают).
  • Проверка неэквивалентности(<>). Возвращает True если множества неэквивалентны.
  • Проверка вхождения(<=). Возвращает True, если первое множество включено во второе.
  • Проверка принадлежности(in). Возвращает True если выражение имеет значение, принадлежащее множеству(2 in M1=true).

Также существует возможность добавлять или исключать из множества элементы:

...
M1:=M1-[2]; //Теперь M1=[1,3,5,8]
M2:=M2+[12]; //Теперь M2=[1,2,4,6,8,12]

Будьте внимательны, не добавляйте в множество элементы, которые не входят в диапазон(хотя Delphi это позволяет).

var m:set of 1..2;
    b:boolean;
begin
m:=[1,2,3,4]; //3 и 4 не входят в диапазон множества.
if 100 in m then b:=true else b:=false;  //100 почему то тоже окажется в множестве
end.

Множества можно использовать для проверки, входит ли какое-нибудь число или буква в некий диапазон:

{$APPTYPE CONSOLE}
var c:char;
begin
readln(c);
if c in ['a'..'z'] then writeln('Vhodit') //Можно было бы написать if((ord(c)>=ord(a))and(ord(c)<=ord(z))) then...
   else writeln('Ne vhodit');
readln;
end.

Строки[править]

Delphi имеет несколько типов для работы со строками:

  • string : основной строковой тип, рекомендуемый в большинстве случаев. Тип string -- это строка, содержимое которой находится в динамической памяти, а размер может изменяться.

Тип String родствен динамическому массиву: длину строки можно узнать вызвав системную функцию Length(s), равно как и установить размер строки(вернее, размер буфера, содержащего саму строку символов).

Подобно динамическому массиву, тип string реализован с подсчетом ссылок. Например, если существует две строки s1 и s2 типа string, и переменной s2 присваивается значение s1, то копирования данных не происходит. Вместо этого, в служебном разделе буфера строки s1, увеличивается на 1 счетчик ссылок, а s2 теперь указывает на тот же буфер, что s1. Вместе с тем, переменные s1 и s2 не являются псевдонимами одна другой. При модификации строки s2, строка s1 остается неизменной. Это обеспечивается механизмом Copy-On-Write(который будет рассмотрен позже).

Тип string - нуль-терминированный, другими словами вслед за последним символом строки всегда следует символ с кодом #0. Это сделано для совместимости с другими языками программирования, так и с операционной системой.

Начиная с Delphi 2009, тип string - это юникодная строка, состоящая из двухбайтовых символов, тогда как в более ранних версиях тип string состоял из однобайтовых символов.

  • ShortString или String[N] где N <= 255 :Этот тип существует только ради совместимости с Turbo Pascal. Строка представляется собой массив фиксированной длин, в нулевом элементе которого записана актуальная длина строки. Один символ равен 1 байту.
  • WideString : Юникодная строка с двухбайтовыми символами, работающая еще со старых версих Delphi. Введена для совместимости с технологией Microsoft COM, и полностью эквивалентна типу C++ BSTR. Полезна для взаимодействия COM, а также для реализации поддержки Unicode в старых версиях Delphi.
  • PChar : Указатель на буфер с нуль-терминированной строкой. В зависимости от версии Delphi является псевдонимом для PAnsiChar(указатель на однобайтовую строку), либо для PWideChar(указатель на Unicode строку). Требует большой острожности в работе.
  • AnsiString : в современных(2009+) версиях Delphi представляет собой однобайтовую строку, по функциональности повторяющую string старых версий Delphi - другими словами, данный тип поддерживает подсчет ссылок, Copy-On-Write, имеер завершающий #0 символ на конце строки. Кроме того, AnsiString неявно характеризуется кодовой страницей, установленной в операционной системе по умолчанию для не не-юникодных программ. Можно явно объявить однобайтовый строковой тип, с указанием нужной кодировки.
type LatinStr=type AnsiString(1252);
     CyrString=type AnsiString(1251);
procedure ansistr();
var  asr:CyrString;//русская кодовая страница
     asl:LatinStr;// латинская кодовая страница

     us:UnicodeString;//то же , что и string
     utf8:Utf8String;//тип Utf8String определен как AnsiString(65001). Будучи однобайтовым, он, тем не менее, поддерживает
    //Unicode , поскольку для кодировки одного символа могут использоваться несколько байт: от одного (для латинницы) и до семи(для иероглифов и т.д.)
begin
 us:='Text:У лукоморья дуб зеленый:αβγδεζηθ';// сначала латинница, потом кириллица, а потом греческие символы
 asr:=us;//asr 'Text:У лукоморья дуб зеленый:????????' Кириллическая кодировка вполне справляется с латинницей и кириллицей, но не с греч. символами
 writeln( StringCodePage(asr)); //1251
 asl:=us;//asl 'Text:? ????????? ??? ???????:aß?de???' Латинница пригодна лишь для латинских символов
 writeln( StringCodePage(asl));//1252
 utf8:=us;//utf8 'Text:У лукоморья дуб зеленый:αβγδεζηθ' Utf8String это однобайтовый юникод, так что может содержать любые символы
 writeln( StringCodePage(utf8));//65001
end;

Наиболее распространенной операцией над строками является конкатенация:

procedure concatstr;
var s1, s2:string;
begin
s1:='Привет';
s2:= ', страна!';
s1:=s1+s2; //s1 теперь содержит 'Привет, страна!'
end;

Строки типа AnsiString, String/UnicodeString, WideString могут быть модифицированы посимвольно, подобно массиву. Для этого нужно указать в квадратных скобках индекс символа, причем индекс первого символа строки будет равен единице. Будьте внимательны : значение индекса не должно превышать длину строки.

procedure testch;
var s:string;
begin
s:='abc';
s[1]:='c';
s[3]:='a'; //теперь s='cba'
end;


Если нужно заполнить строку наперед известным количеством символов, рекомендуется явно задать длину этой строки, прежде, чем начать посимвольное заполнение

procedure strc;
var s:string;
   ix:integer;
    c:char;
begin
SetLength(s,26);//по числу букв латинского алфавита
ix:=1;
for c in ['a'..'z'] do begin
 s[ix]:=c;
 Inc(ix);//увеличение ix на 1
end;

//s='abcdefghijklmnopqrstuvwxyz'
end;

Если же необходимо построить строку из большого количества фрагментов, лучше использовать класс TStringBuilder

procedure strb;
var s:string;
    c:char;
    sb:TStringBuilder;
begin

 sb:=TStringBuilder.Create(20);
 try
  for c in ['a'..'z'] do  sb.Append(c).Append(',');
  s:=sb.ToString();

 finally sb.Free(); end;
end;

К строкам применимы операции отношения(=, <>, >, <, >=, <=). Если для некоторых строк s1=s2, то они побайтово идентичны: совпадает их длина, так и содержимое, с учетом регистра(строчные буквы различают с прописными).

Операторы >, <, >=, <= сравнивают строки в некоторой бинарной форме. Например, строка 'B' больше всякой строки, начинающейся с 'A', так как код символа B больше. А при сравнеии строк с различной длиной, когда более длинная отличается от короткой лишь теми символами, которые по своему положению не имеют соответствия в короткой - как например 'ABCDEF' и 'ABCDEFGH', большей признается строка, имеющая наибольшую длину. 'ABCDEF'<'ABCDEFGH'


Использование строк во внешних API[править]

Если вы уверены, что некоторая функция , принимающая нуль-терминированную строку типа PWideChar/PAnsiChar, не будет ее модифицировать, просто сделайте преобразование из UnicodeString/AnsiString к типу Pointer;

 
 s:='someval';
 SomeExternalFun(Pointer(s)); //преобразование к Pointer быстрее и безопаснее, даже если входной парамерт функции определен как PChar
// будьте внимательны, и не пользуйтесь этим приемом, если внешняя функция модифицирует вхдолной параметр

Если же, напротив, внешняя функция пишет строку в некоторый буфер, то можно поступить следующим образом

 UniqueString(s);//убеждаемтся, что у нас уникальный буфер
 SetLength(s,256);//установим максимальный размер буфера 
 l:= SomeExternalFun(Pointer(s), length(s) ); //первым параметром -- указатель на буфер, вторым -- размер буфера,
  // а в возвращаемом значении мы ожидаем фактическую длину строки
 SetLength(s,l);//усекаем буфер до фактического размера записанной строки

Функции и процедуры для работы со строками[править]

Функция Параметры Результат
Delete(var S: string;Index,Count:Integer) s - строка,из которой будем удалять; Index - символ, начиная с которого нужно удалять Count - количество символов Удаляет начиная с N-ого символа I символов.
Insert(Source:string;var S:string;Index:Integer) Source - строка для вставки, S - Строка, в которую вставляем, Index - начиная с какого символа вставляем Вставляет в одну строку другую
Copy(S; Index, Count: Integer): string; s- строка или массив, откуда мы копируем; Index - начиная с какого символа копируем; Count - количество копируемых символов Копирует Count символов из строки S.
Pos(Substr:string; S:string): Integer; Substr - подстрока, которую надо найти, S - строка, в кторой ищем Функция ищет подстроку Substr в строке S и возращает символ, с ктоторого она начинается, или 0 если такой подстроки нет.
Length(s:string):integer; s - строка, длину который нужно определить Определяет длину строки