Строки

1. Введение

Строка представляет собой последовательность символов. Язык Паскаль поддерживает следующие разновидности строк:

Тип Максимальная длина Требуемая память Используется для
ShortString 255 2-256 байт обратной совместимости
AnsiString 231 4 байта – 2 Гб преставления строк, содержащих ANSI-символы
WideString 230 4 байта – 2 Гб преставления строк, содержащих символы Unicode

Различные типы строк совместимы и могут быть совместно использованы в выражениях и присваиваниях, компилятор автоматически выполняет требуемые преобразования. Однако при передаче строки в подпрограмму по ссылке, типы должны быть идентичны.

Тип string – это обобщённый тип, который на платформе Win32 эквивалентен типу AnsiString.

Строковый литерал – это последовательность символов в апострофах. Два апострофа вместе задают так называемую пустую строку, т.е. строку, в которой нет ни одного символа. Если в строку надо вставить апостроф, он удваивается.

2. Короткая строка

Короткая строка ShortString поддерживается для обратной совместимости. Короткая строка может иметь длину от 0 до 255 байт. Хотя длина строки может меняться динамически, память под строку выделяется статически в размере 256 байт. Первый байт из этих 256 содержит длину строки, а остальные – собственно символы строки.

Язык Паскаль поддерживает подтипы типа короткая строка, которые могут иметь любую длину в пределах от 1 до 255. Эта длина указывается при объявлении типа в квадратных скобках. var s: string[10]; // Строка не более, чем из 10 символов

Для строк подобных типов память выделяется по минимуму – в данном случае 10 + 1 символов (1 символ используется для хранения реальной длины строки). При присваивании длина строки усекается до максимального размера строки.

3. Длинная строка

Длинная строка – string, AnsiString – представляет собой динамически распределяемую строку, максимальная длина которой ограничивается только объёмом доступной памяти.

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

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

Поскольку длинные строки относятся к динамическим типам, они по-разному ведут себя при передаче строки в подпрограмму с параметрами переменными и выходными параметрами (для статических типов никакой разницы между этими классами параметров нет). Параметр-переменная подразумевает, что с помощью этого параметра данные могут быть переданы как в подпрограмму, так и из неё. Поэтому значение фактического параметра передаётся в подпрограмму. Выходной параметр подразумевает, что с помощью этого параметра данные могут быть переданы только из подпрограммы. Поэтому значение фактического параметра в подпрограмму не передаётся – строка заново инициализируется начальным значением (пустой строкой). var str: string; procedure P1(var s: string); begin writeln(s); s := 'C & C++'; end; procedure P2(out s: string); begin writeln(s); s := 'Assembler'; end; begin str := 'Delphi'; P1(str); // Выведется 'Delphi'. str = 'C & C++' P2(str); // Выведется ''. str = 'Assembler' end;

4. Операции и операторы для работы со строками

К строкам можно применять все операции сравнения. Для строк выполняется лексикографическое сравнение, т.е. меньше оказывается та строка, в которой раньше встречается меньший символ. Если одна строка длиннее, а все начальные символы равны, больше оказывается более длинная строка. 'ab' > 'a' = true '12' < '5' = true

Ещё одна операция, применяемая к строкам, – операция конкатенации. Операция обозначается знаком + и представляет собой сцепление строк. 'язык ' + 'Паскаль' = 'язык Паскаль'

В PascalABC определена также операция «умножения». '*' * 10

Для перебора элементов строки можно использовать оператор foreach…in: foreach <элемент> in <строка> do <оператор>

Элемент должен иметь тип char. Он не может быть изменён в теле цикла.

Рассмотрим для примера подсчёт количества символов строки, равных заданному. function CharCount(s: string; ch: char): integer; var c: char; begin result := 0; foreach c in s do if c = ch then result := result + 1; end;

Другой способ получить доступ к элементам строки – использовать операцию индексирования. Если s – строка, то выражение s[i] позволяет обратиться к i-ому символу строки, причём, возможно и прочитать i-ый символ, и записать новое значение. Индексы элементов строки лежат в пределах от 1 до Length(s). Однако если значение i слишком велико, операция не имеет эффекта при записи и возвращает неопределённый результат при чтении. procedure Change(var s: string); var i: integer; begin for i := 1 to Length(s) do if ('a' <= s[i]) and (s[i] <= 'y') then // Выбираем один способ – сравнение if s[i] in ['a'..'y'] then // или проверка на вхождение в множество s[i] := chr(ord(s[i]) + 1) else if s[i] = 'z' then s[i] := 'a'; end;

Над строками в PascalABC определена также операция взятия среза. Срез – это набор элементов строки, расположенных последовательно или с некоторым шагом. str[from : to] str[from : to : step]

При этом символ с номером from включается в результат, а символ с номером to – нет. str := 'asedokimu'; str := str[2 : 5]; // sed str := 'asedokimu'; str := str[1 : Length(str) : 2]; // aeoi str := 'asedokimu'; str := str[1 : Length(str) + 1 : 2]; // aeoiu

5. Стандартные подпрограммы работы со строками

Функция Length возвращает длину строки. function Length(s: string): integer;

Процедура SetLength меняет длину строки на указанную. Для длинных строк выделяется новый участок памяти заданного размера. Если новый размер больше старого, содержимое новых элементов строки не определено. procedure SetLength(var s: string; newLength: integer);

Функция Concat объединяет несколько строк в одну. Тот же результат можно получить с помощью операции +, которая работает быстрее. function Concat(s1: string; s2: string [ … sn: string]): string;

Функция Pos позволяет найти вхождение одной строки в другую. Она возвращает индекс элемента, с которого начинается вхождение подстроки, или 0, если вхождений нет. function Pos(substr, str: string; from: integer := 1): integer;

Функция LastPos позволяет найти последнее вхождение одной строки в другую. Она возвращает индекс элемента, с которого начинается вхождение подстроки, или 0, если вхождений нет. function LastPos(substr, str: string): integer;

Функция Copy копирует часть строки, содержащую count символов, начиная с элемента с номером index. Если index больше количества символов в строке, функция возвращает пустую строку. Если count задаёт больше символов, чем доступно, то функция возвращает часть строки до конца строки. function Copy(s: string; index: integer; count: integer): string;

Процедура Insert вставляет одну строку в другую. Вставленная строка размещается, начиная с номера index. Если index меньше 1, то строка вставляется в начало. Если index больше длины строки, строка вставляется в конец. procedure Insert(source: string; var dest: string; index: integer);

Процедура Delete удаляет часть строки, содержащую count символов, начиная с элемента с номером index. Если index больше количества символов в строке или меньше 1, то ничего не удаляется. Если count задаёт больше символов, чем доступно, то процедура удаляет все символы до конца строки. Если count меньше или равно 0, ничего не удаляется. procedure Delete(var s: string; index: integer; count: integer);

Функции Trim, TrimLeft и TrimRight возвращают строку с удалёнными начальными и конечными, начальными и конечными пробелами соответственно. function Trim(s: string): string; function TrimLeft(s: string): string; function TrimRight(s: string): string;

Функции LowerCase, AnsiLowerCase и WideLowerCase преобразуют прописные символы строки в строчные. Функция LowerCase меняет только символы от ‘A’ до ‘Z’, две другие функции воздействуют на все буквы, в том числе буквы национальных алфавитов. Функция AnsiLowerCase работает с однобайтовыми символами ANSI, а функция WideLowerCase – с двухбайтовыми символами Unicode. function LowerCase(const s: string): string; function AnsiLowerCase(const s: string): string; function WideLowerCase(const s: WideString): WideString;

Функции UpperCase, AnsiUpperCase и WideUpperCase преобразуют строчные символы строки в прописные. function UpperCase(const s: string): string; function AnsiUpperCase(const s: string): string; function WideUpperCase(const s: WideString): WideString;

Функции StrToInt, StrToInt64 и StrToReal преобразуют строковое представление целого/вещественного числа к числовому значению. function StrToInt(s: string): integer; function StrToInt64(s: string): int64; function StrToReal(s: string): real;

Функции TryStrToInt, TryStrToInt64 и TryStrToReal преобразуют строковое представление целого/вещественного числа к числовому значению и записывают его в переменную value. При невозможности преобразования возвращается false, иначе – true. function TryStrToInt(s: string; var value: integer): boolean; function TryStrToInt64(s: string; var value: int64): boolean; function TryStrToReal(s: string; var value: real): boolean;

6. Примеры

Пример 1. Сформировать строку, состоящую из символов исходной строки, не входящих в заданный набор символов (набор также задаётся строкой). procedure NotEntered(source, symbols: string; var dest: string); var i: integer; begin dest := ''; for i := 1 to Length(source) do if Pos(source[i], symbols) = 0 then dest := dest + source[i]; end;

Пример 2. Вывести по отдельности слова, входящие в строку. procedure WriteWords(str: string); var wrd: string; p: integer; begin str := Trim(str); // Удаляем начальные и конечные пробелы while str <> '' do // Пока есть символы в строке begin p := Pos(' ', str); // Ищем пробел if p > 0 then // Если он есть begin wrd := Copy(str, 1, p - 1); // Копируем слово до пробела Delete(str, 1, p); // Удаляем слово end else // Если пробела нет begin wrd := str; // Копируем всю оставшуюся строку str := ''; // Удаляем строку end; writeln(wrd); str := TrimLeft(str); // Удаляем начальные пробелы end; end;