Программирование на языке 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 - строка, длину который нужно определить | Определяет длину строки |