Версия для печати


Символьные и строковые типы данных
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1061

Andrew Fionik
дата публикации 01-10-2004 17:30

урок из цикла: Азы Delphi.

Символьные и строковые типы данных

Итак, с числами мы разобрались. Настал черед текста. Delphi позволяет манипулировать текстовыми данными в виде отдельных символов, а также в виде последовательностей символов - строк.

Тут следует сделать небольшое лирическое отступление по поводу изложения материала. Дело в том, что тема хранения текстовых данных чрезвычайно обширна как по количеству, так и по сложности. Если сразу же вывалить ее на голову простого чайника во всей полноте, то он мало чего поймет и, скорее всего, запутается. Поэтому мы поступим, как делают в школе на уроках физики. Вначале законы физики объясняются по-простому, в виде Ньютоновской механики, а уж потом, много позже, начинаем рассказывать релятивистские сказки от сказочника Эйнштейна, и видим, что первоначальное мировоззрение оказывается всего лишь упрощенным представлением настоящей картины мира. Так же поступим и здесь.

Тип данных Char

Базовый тип данных (т.е. то на чем основываются все остальные текстовые типы данных) - Char. Переменная типа Char может хранить всего один символ, один из набора 256 символов. Каждый символ имеет свой код. На самом-то деле компьютер оперирует не символами, (он не знает что это такое) а кодами символов. Например, во время печати символа с определенным кодом он ищет графическое изображение символа в таблице изображений символов за определенным номером, а потом выводит именно это графическое изображение на дисплей. Символы можно сравнивать между собой на равенство и на определение, какой больше или меньше, тогда компьютер опять же сравнивает не сами символы, а их коды.

Приблизительно таблицу символов можно описать примерно так. В начале идут различные служебные управляющие символы, которые не имеют графического представления. Например, это символы табуляции, возврата каретки, перевода строки и т.п. Потом идут знаки препинания '!', '?', '.' и т.п. Далее идут цифры '1', '2', '3' и т.д. Следующими идут литеры английского алфавита 'A', 'B', 'C',...,'Z', 'a', 'b', 'c',..., 'z'. Завершают таблицу литеры национального алфавита: 'А', 'Б', 'В',...,'Я','а', 'б', 'в',...,'я'.

Следует заметить, что существует масса таких таблиц символов. Разные системы могут по-разному интерпретировать коды символов по той простой причине, что они руководствуются разными таблицами символов. В этом опусе мы будем использовать таблицу символов называемую Windows Code Page 1251 (Cyrillic). Это таблица символов для кириллицы в системах Microsoft Windows. Это означает что в системе Windows каждый символ с определенным кодом будет интерпретироваться одинаково для всех программ, если они конечно не реализуют какую-то свою, специфическую обработку символов.

Как мы уже говорили раньше, переменные символьных типов могут содержать только один символ, а не два и не три и т.д. Пример использования типа Char:

program CharDemo1;
var
  A:Char;
begin
  A:='Z';
  WriteLn(A);
end.

Эта программа присваивает переменной типа Char значение символа 'Z' а потом выводит его на консоль.

А вот эта программа не будет скомпилирована компилятором по причине ошибки компиляции. Переменной A может быть присвоено значение только одного символа.

program CharDemo2;
var
  A:Char;
begin
  A:='ZZZ';
  WriteLn(A);
end.

Для типа данных Char определены также две операции - получение кода символа из переменной типа Char и преобразование кода символа в значение типа Char. Эти операции реализованы двумя функциями стандартной библиотеки - Ord и Chr.

Функция Ord принимает один параметр типа Char а возвращает значение типа Byte которое и представляет собой код символа. Функция Chr делает все с точностью наоборот - принимает значение типа Byte а возвращает значение типа Char.

Примеры использования функций Ord и Chr:

program CharDemo3; // Наименование программы - CharDemo3.
{$APPTYPE CONSOLE} { Инструкция компилятору Delphi генерировать консольную
                     программу.}
const
  A='M'; // Объявим константу A значение которой 'M'
var
  B:Char; // Объявим переменную B типа Char.
  CodeA,CodeB:Byte; { Объявим две переменных CodeA и CodeB типа Byte.
                      В них мы будем хранить коды символов.}
begin
  WriteLn('Value of A is ',A); // Напечатаем содержимое константы A
  CodeA:=Ord(A); // Присвоим переменной CodeA код символа содержащегося в A
  WriteLn('Code of A is ',CodeA); // Напечатаем код символа содержащегося в A
  CodeB:=CodeA+1; { Присвоим перменной CodeB код символа следующего за символом
                   в A.}
  B:=Chr(CodeB); { Преобразуем код символа в значение типа Char
                   и присвоим его B.}
  WriteLn('Value of B is ',B); // Напечатаем значение B.
  WriteLn('Code of B is ', CodeB); // Напечатаем код символа лежащего в B.
  CodeB:=CodeA-1; {Присвоим перменной CodeB код символа предыдущего перед
                   символом в A}
  B:=Chr(CodeB); // Снова преобразуем код символа в переменную типа Char
  WriteLn('Value of B is ',B); // Снова печатаем содержимое B...
  WriteLn('Code of B is ', CodeB); //... и CodeB, они уже немножко другие
  ReadLn; // ждем нажатия клавиши Enter чтобы завершить программу
end.

При выполнении этой программы должен быть выдан вот такой текст:
Value of A is M
Code of A is 77
Value of B is N
Code of B is 78
Value of B is L
Code of B is 76

Тип данных String

Переменная типа String представляет собой последовательность символов - строку. Мы можем определять длину строки, можем добавлять к строке другие строки и символы, можем заменять произвольные части строки на другие строки и символы, можем удалять части строки.

Пример объявления переменной типа String:

program StringDemo1; // название программы
{$APPTYPE CONSOLE} // инструкция компилятору генерировать консольное приложение
var // секция объявления переменных
  A:String; // объявляем переменную A типа String
begin // начало тела программы
  A:='Hello world!'; // присваиваем переменной A строку 'Hello world!'
  WriteLn(A); // выводим содержимое переменной A на экран
  ReadLn; // ждем нажатия Enter
end. // конец тела программы



Операции над строками

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

Подпрограммы

Забегая немного вперед, следует указать, что многие операции над строками выполняются с помощью различных подпрограмм. Подпрограмма - кусочек программного кода, который имеет идентификатор и может быть вызван по нему для выполнения какой-либо задачи. Зачастую подпрограмма требует передать ей какие-нибудь параметры. В качестве параметров используется либо выражение, либо идентификатор чего-либо. Например, идентификатор переменной или константы. Подпрограммы подразделяются на процедуры и функции. Разница между ними лишь в наличии возвращаемого результата. Функции возвращают результат своего выполнения, а процедуры не возвращают никакого результата, только выполняют какое-то действие. Например, Sin - функция, которая вычисляет значение синуса переданного ей параметра и возвращает результат вычисления в виде вещественного значения. В противоположность ей WriteLn является процедурой, которая никаких значений не возвращает, но выполняет действие - вывод значений переданных ей параметров на консоль. Процедуры вызываются следующим образом:

Имя_процедуры(Параметр1, Параметр2, ..., ПараметрN);

Вызов функции может быть использован внутри какого-либо выражения (для его вычисления). Также вызов функции может быть использован в операторе присваивания, чтобы присвоить какой-нибудь переменной результат выполнения функции.

Имя_переменной:=Имя_функции(Параметр1, Параметр2, ..., ПараметрN);

Присваивание значения строке, получение содержимого строки

Строковой переменной может быть присвоено значение, как и любой другой переменной. Единственное ограничение состоит в том, что тип значения должен быть String или Char (что такое один символ как не строка длиной 1).

program StringDemo01;
{$APPTYPE CONSOLE}
const
  A='Hello, world'; // объявляем константу содержащую значение 'Hello world'
var
  B,C:String; // объявляем две переменные типа String - B и C
begin
  B:=A; // присваиваем значение константы A переменной B
  WriteLn(B); // выводим на консоль содержимое переменной B
  C:=B; // присваиваем содержимое переменной B в переменную С
  WriteLn(C); // выводим на консоль содержимое переменной C
  ReadLn; // ожидаем нажатия Enter
end;



Получение длины строки

Для определения длины строки в символах используется функция стандартной библиотеки Length. Функция Length получает на вход один единственный параметр, идентификатор строковой переменной или выражение строкового типа. Результат функции, количество символов в строке, имеет тип Integer.

Формат вызова:

Length(идентификатор переменной или выражение типа String)

Пример:

program StringDemo02;
{$APPTYPE CONSOLE}
const
	A='Hello, world';
var
	B:String;
	L:Integer; {сюда мы будем заталкивать результат функции Length,
		количество символов}
begin
	B:=A; // присваиваем значение константы A переменной B
	L:=Length(B); // присваиваем переменной L количество символов в B
	// выводим на консоль содержимое переменных B и L
	WriteLn('Length of "',B,'" is ', L);
	ReadLn; // ожидаем нажатия Enter
end;



Получение и установка отдельного символа строки

К символам строки можно обращаться по их индексу (позиции в строке). Индекс первого символа строки 1. Индекс последнего символа строки будет равен результату вызова функции Length, которой в качестве параметра будет передана строка. Обратите внимание на то, что индекс первого символа 1, а не 0 как обычно делается в различных структурах данных, которые могут содержать много элементов (вообще-то больше одного). При попытке обращения к символу с индексом меньше 0 или больше Length(идентификатор строки) возникает ошибка времени выполнения.

Формат обращения к символу строки:
S[I]
... где S - идентификатор строковой переменной, а I - индекс символа.

Выражение вида S[I], где S - идентификатор строковой переменной, возвращает результат типа Char.

Пример доступа к символу строки по индексу:

program StringDemo03;
{$APPTYPE CONSOLE}
const
  A='Hello, world';
var
  B:String;
  I:Integer;
  C:Char;
begin
  B:=A;
  // Выводим значение строки B и первого символа строки B.
  WriteLn('First character of string "', B, '" is "',B[1],'"');
  // вычисляем индекс последнего символа в строке B
  I:=Length(B);
  // присваиваем переменной C значение последнего символа строки B
  C:=B[I];
  // Выведем значение последнего символа строки B
  WriteLn(C);
  {Теперь поменяем значения последнего и первого символов строки B
   друг на друга}
  // присвоим значение первого символа строки B последнему символу
  B[Length(B)]:=B[1];
  {Теперь значение последнего символа строки B потерялось поскольку было
   переписано значением первого символа. Однако копия этого значения
   осталась в переменной C, поэтому мы присвоим значение переменной C
   первому символу строки B}
  B[1]:=C;
  // Выведем измененную строку B
  WriteLn(B);
  ReadLn; // ожидаем нажатия Enter
end;

Должно вывестись следующее:
First character of string "Hello, world" is "H"
d
dello, worlH


Слияние двух строк (конкатенация)

Строки можно складывать как числовые переменные, используя оператор +. Результатом сложения является более длинная строка, включающая в себя содержимое обеих строк в порядке их употребления в операции сложения. Также существует функция Concat которая выполняет в точности то же действие что и +.

Формат операции конкатенации двух строк:
A+B
или 
Concat(A,B)
... где A - идентификатор одной строки или строковое выражение, а B - идентификатор другой строки или строковое выражение.

Пример:

program StringDemo04;
{$APPTYPE CONSOLE}
var
  A,B,C,D:String;
begin
  C:='Hello';
  D:='world';
  A:=C+D;
  B:=C+', '+D;
  C:=D+C;
  D:=Concat(A+B);
  WriteLn(A);
  WriteLn(B);
  WriteLn(C);
  WriteLn(D);
  ReadLn;
end;

Результат вывода должен быть следующим:
Helloworld
Hello, world
worldHello
HelloworldHello, world


Выделение подстроки из строки

Иногда бывает необходимо получить часть строки, а не всю ее целиком. Для этого существует функция Copy. Функция Copy получает на вход три параметра - строку, позицию с которой надо начинать копирование и количество символов которые надо скопировать. Формат вызова функции Copy:

Copy(Source,Index,Count);
... где Source - идентификатор строковой переменной или строковое выражение часть которого должна быть скопирована, Index - позиция с которой должно быть осуществлено копирование, Count - количество символов которое должно быть скопировано. Пример выделения подстроки:

program StringDemo04;
{$APPTYPE CONSOLE}
var
	A,B:String;
begin
	A:='Hello, world';
	B:=Copy(A,2,5);
	A:=Copy('Delphi rulez',1,6)+' great';
	WriteLn(B);
	WriteLn(A);
	ReadLn;
end;

На консоль должно быть выведено следующее:
ello,
Delphi great


Вставка одной строки внутрь другой

Вставка строки выполняется процедурой Insert. Процедура получает на вход три параметра: строку, которую нужно вставить, строку в которую должна быть произведена вставка и позицию, в которой должна быть произведена вставка. Формат вызова:

Insert(Source,Target,Position);

Обратите внимание, что это уже не функция, а процедура. Она не возвращает никакого результата, а модифицирует содержимое Target. Еще одна особенность использования процедуры, а не функции заключается в том, что в качестве Target можно передавать только идентификатор переменной, но нельзя передавать выражение или идентификатор константы. Процедура Insert присваивает результат своей работы Target, а присвоить что-либо константе или выражению невозможно по определению.

Пример использования Insert

program StringDemo05;
{$APPTYPE CONSOLE}
var
	A:String;
begin
	A:='Happy friends';
	Insert(' tree',A,6);
	WriteLn(A);
	ReadLn;
end;

В результате выполнения этой программы должна быть выведена строка:
Happy tree friends


Удаление части строки

Часть строки может быть удалена с помощью процедуры Delete. Формат вызова процедуры Delete:

Delete(Target,Index,Count);
... где Target - идентификатор строковой переменной, из которой удаляются символы, Index - позиция, начиная с которой производится удаление, Count - количество удаляемых символов. Пример использования Delete:

program StringDemo06;
{$APPTYPE CONSOLE}
var
	A:String;
begin
	A:='Happy friends';
	Delete(A, 3, 5);
	WriteLn(A);
	ReadLn;
end;

Результат вывода должен быть: Hariends

Определение позиции вхождения одной строки в другую

Иногда бывает нужно определить содержится ли одна строка в другой, а также позиция с которой содержимое строк совпадает. Для этого применяется функция Pos. Формат вызова:

Pos(SubStr, Str) ... где Str - строка, в которой производится поиск SubStr. Функция возвращает позицию вхождения в качестве результата. Если строка SubStr не находится в Str, то результат выполнения функции будет равен 0. Функция различает регистр букв - большой и маленький, т.е. подстрока 'dancing' будет найдена в 'We dancing in shadows', но не будет найдена в 'We Dancing In Shadows'.

Пример использования Pos:

program StringDemo07;
{$APPTYPE CONSOLE}
var
	I:Integer;
begin
	I:=Pos('win','Good guys only win in movies');
	WriteLn(I);
	ReadLn;
end;

Должно быть напечатано число 16.

Изменение регистра символов

Каждый символ, представляющий собой букву, имеет регистр - верхний или нижний. Например, буква "а" может быть заглавной "А" и прописной "а". Заметим что "А" и "а" являются различными символами. Про заглавные буквы говорят, что они имеют верхний регистр. Про прописные буквы говорят, что они имеют нижний регистр. В некоторых случаях нужно преобразовать отдельную строку или символ в верхний или нижний регистр. Это делают функции UpCase, UpperCase, LoCase, LowerCase.

Формат вызова функции UpCase: UpCase(Source)
Source - идентификатор переменной или выражение типа Char. Результат, возвращаемый функцией, также является значением типа Char и представляет собой значение Source преобразованное в верхний регистр.
Формат вызова функции LoCase: LoCase(Source)
Source - идентификатор переменной или выражение типа Char. Результат, возвращаемый функцией, также является значением типа Char и представляет собой значение Source преобразованное в нижний регистр.
Формат вызова функции UpperCase: UpperCase(Source)
Source - идентификатор переменной или выражение типа String. Результат, возвращаемый функцией, также является значением типа String и представляет собой значение Source преобразованное в верхний регистр.
Формат вызова функции LowerCase: LowerCase(Source)
Source - идентификатор переменной или выражение типа String. Результат, возвращаемый функцией, также является значением типа String и представляет собой значение Source преобразованное в нижний регистр.
Пример использования функций изменения регистра:

program StringDemo07;
const
  SourceString='Hello world';
var
  TargetString:String;
begin
  // присвоим TargetString значение SourceString переведенное в нижний регистр
  TargetString:=LowerCase(SourceString);
  // напечатаем содержимое TargetString
  WriteLn(TargetString);
  // напечатаем значение SourceString переведенное в верхний регистр
  WriteLn(UpperCase(SourceString));
  // Возведем первый символ TargetString в верхний регистр
  TargetString[1]:=UpCase(TargetString[1]);
  // возведем последний символ TargetString в верхний регистр
  TargetString[Length(TargetString)]:=UpCase(TargetString[Length(TargetString)]);
  // напечатаем содержимое TargetString
  WriteLn(TargetString);
  // понизим регистр первого символа TargetString
  TargetString[1]:=LoCase(TargetString[1]);
  // напечатаем содержимое TargetString
  WriteLn(TargetString);
  // Ждем нажатия Enter
  ReadLn;
end.

Вывод должен быть следующим:
hello world
HELLO WORLD
Hello worlD
hello worlD


Сравнение строк на равенство или больше/меньше

Строки можно сравнивать между собой как если бы они были числами. Возможны следующие операции сравнения: Результатом сравнения является значение типа Boolean, которое может быть только одним из двух - TRUE (Истина) и FALSE (Ложь).

Правила сравнения:

Сравнение на предмет равенства
  1. Сравнивается длина строк. Если длина разная, то значит, что строки не равны. Возвращается результат FALSE.
  2. Если длина одинаковая, производится посимвольное сравнение строк. Как только в какой-либо позиции сравниваемых строк находятся разные символы, возвращается FALSE - строки не равны.
  3. Если длина строк одинаковая и все символы на соответствующих позициях равны друг-другу, то строки считаются равными. Возвращается результат TRUE.
Сравнение на предмет неравенства

Производится сравнение на предмет равенства, затем результат "переворачивается вверх ногами". Вместо TRUE возвращается FALSE и наоборот.

Сравнение на "меньше"

Поочередно сравниваются коды символов, стоящих на соответствующих позициях. Если одна из строк больше не имеет символов, или символ, стоящий в соответствующей позиции имеет меньший код, то считается что эта строка "меньше".

Пример:
Строка 'Ivan' меньше строки 'Ivar', т.к. первые три символа равны, а четвертый символ в первой строке ('n') меньше чем четвертый символ ('r') во второй строке.

Пример:
Строка 'Doom' меньше чем строка 'Doomsday' потому что четыре первых символа совпадают, но пятого символа в первой строке уже нет, а во второй есть хоть какой-то.

Сравнение на "больше"

Поочередно сравниваются коды символов, стоящих на соответствующих позициях. Если одна из строк еще имеет символы, в то время как другая больше не имеет символов, или символ, стоящий в соответствующей позиции имеет больший код, то считается что первая строка "больше".

Нечеткие сравнения на "меньше или равно" и "больше или равно"

Фактически "меньше или равно" означает "не больше", а "больше или равно" означает "не меньше". Соответственно производится сравнение на предмет "больше" или "меньше" и результат инвертируется.

Пример:

program StringDemo08;
const
  C1='Hello World';
  C2='Hello day';
  C3='Hello baby';
  C4='Hello World';
begin
  // печатаем результат сравнения на равенство
  WriteLn(C1=C4);
  WriteLn(C1=C2);
  // печатаем результат сравнения на неравенство
  WriteLn(C3<>C4);
  WriteLn(C1<>C4);
  // печатаем результат сравнения на "меньше"
  WriteLn(C2<C3);
  WriteLn(C1<C4);
  // печатаем результат сравнения на "больше"
  WriteLn(C2>C3);
  WriteLn(C1>C4);
  ReadLn;
end.

Результат должен быть следующий:
TRUE
FALSE
TRUE
FALSE
FALSE
FALSE
TRUE
FALSE