Глава 18. Строки с завершающим нулем
В Borland Pascal поддерживается класс символьных строк, ко-
торые называются строками, завершающимися нулем. Благодаря расши-
ренному синтаксису Borland Pascal и модулю Strings ваши программы
(как для DOS, так и для Windows) могут использовать строки с за-
вершающим нулем путем задания в операторе uses модуля Strings.
Что такое строка с завершающим нулем?
В Borland Pascal строки обычного типа (String) хранятся как
байт длины, за которым следует последовательность символов. Мак-
симальная длина строки в Паскале равна 255 символам. Таким обра-
зом, строка Паскаля занимает от 1 до 256 байт памяти.
Строки с завершающим нулем не содержат байта длины. Вместо
этого они состоят из последовательности ненулевых символов, за
которыми следует символ NULL (#0). Никаких ограничений на длину
строк с завершающим нулем не накладывается, но 16-разрядная архи-
тектура DOS и Windows ограничивает их размер 65535 символами.
Функции модуля Strings
Borland Pascal не имеет встроенных подпрограмм, предназна-
ченных специально для работы со строками с завершающим нулем. Эти
функции вы можете найти в модуле Strings. Среди них вы найдете
функцию StrPCopy, которую можно использовать для копирования
строки Паскаля в строку с завершающим нулем, и StrPos, используе-
мую для преобразования строки с завершающим нулем в строку Паска-
ля. Приведем краткое описание каждой функции:
Функции модуля Strings
---------------T------------------------------------------------
¦ Функция ¦ Описание ¦
+--------------+------------------------------------------------+
¦ StrCat ¦ Добавляет исходную строку к концу целевой стро-¦
¦ ¦ ки и возвращает указатель на целевую строку. ¦
+--------------+------------------------------------------------+
¦ StrComp ¦ Сравнивает две строки S1 и S2. Возвращает¦
¦ ¦ значение < 0, если S1 < S2, равное 0, если S1 =¦
¦ ¦ S2 и > 0, если S1 > S2. ¦
+--------------+------------------------------------------------+
¦ StrCopy ¦ Копирует исходную строку в целевую строку и¦
¦ ¦ возвращает указатель на целевую строку. ¦
+--------------+------------------------------------------------+
¦ StrECopy ¦ Копирует исходную строку в целевую строку и¦
¦ ¦ возвращает указатель на конец целевой строки. ¦
+--------------+------------------------------------------------+
¦ StrIComp ¦ Сравнивает две строки без различия регистра¦
¦ ¦ символов. ¦
+--------------+------------------------------------------------+
¦ StrLCat ¦ Присоединяет исходную строку к концу целевой¦
¦ ¦ строки. При этом обеспечивается, что длина ре-¦
¦ ¦ зультирующей строки не превышает заданного мак-¦
¦ ¦ симума. Возвращается указатель на строку-ре-¦
¦ ¦ зультат. ¦
+--------------+------------------------------------------------+
¦ StrLComp ¦ Сравнивает две строки с заданной максимальной¦
¦ ¦ длиной. ¦
+--------------+------------------------------------------------+
¦ StrLCopy ¦ Копирует заданное число символов из исходной¦
¦ ¦ строки в целевую строку и возвращает указатель¦
¦ ¦ на целевую строку. ¦
+--------------+------------------------------------------------+
¦ StrEnd ¦ Возвращает указатель на конец строки, то есть¦
¦ ¦ указатель на завершающий строку нулевой символ.¦
+--------------+------------------------------------------------+
¦ StrDispose ¦ Уничтожает ранее выделенную строку. ¦
+--------------+------------------------------------------------+
¦ StrLen ¦ Возвращает длину строки. ¦
+--------------+------------------------------------------------+
¦ StrLIComp ¦ Сравнивает две строки с заданной максимальной¦
¦ ¦ длиной без различия регистра символов. ¦
+--------------+------------------------------------------------+
¦ StrLower ¦ Преобразует строку в нижний регистр и возвраща-¦
¦ ¦ ет указатель на нее. ¦
+--------------+------------------------------------------------+
¦ StrMove ¦ Перемещает блок символов из исходной строки в¦
¦ ¦ целевую строку и возвращает указатель на целе-¦
¦ ¦ вую строку. Два блока могут перекрываться. ¦
+--------------+------------------------------------------------+
¦ StrNew ¦ Выделяет для строки память в динамически рас-¦
¦ ¦ пределяемой области. ¦
+--------------+------------------------------------------------+
¦ StrPas ¦ Преобразует строку с завершающим нулем в строку¦
¦ ¦ Паскаля. ¦
+--------------+------------------------------------------------+
¦ StrPCopy ¦ Копирует строку Паскаля в строку с завершающим¦
¦ ¦ нулем и возвращает указатель на строку с завер-¦
¦ ¦ шающим нулем. ¦
+--------------+------------------------------------------------+
¦ StrPos ¦ Возвращает указатель на первое вхождение задан-¦
¦ ¦ ной подстроки в строке, или nil, если подстрока¦
¦ ¦ в строке не содержится. ¦
+--------------+------------------------------------------------+
¦ StrRScan ¦ Возвращает указатель на последнее вхождение¦
¦ ¦ указанного символа в строку, или nil, если сим-¦
¦ ¦ вол в строке отсутствует. ¦
+--------------+------------------------------------------------+
¦ StrScan ¦ Возвращает указатель на первое вхождение ука-¦
¦ ¦ занного символа в строку, или nil, если символ¦
¦ ¦ в строке отсутствует. ¦
+--------------+------------------------------------------------+
¦ StrUpper ¦ Преобразует строку в верхний регистр и возвра-¦
¦ ¦ щает указатель на нее. ¦
L--------------+-------------------------------------------------
Использование строк с завершающим нулем
Строки с завершающим нулем хранятся в виде символьных масси-
вов с нулевой базой (начинающихся с 0) с индексом целого типа, то
есть в виде массива:
array[0..X] of Char;
где X - положительное ненулевое целое число. Такие массивы назы-
ваются символьными массивами с нулевой базой. Приведем некоторые
примеры описаний символьных массивов с нулевой базой, которые мо-
гут использоваться для хранения завершающихся нулем строк.
type
TIdentifier = array[0..15] of Char;
TFileName = array[0..79] of Char;
TMemoText = array[0..1023] of Char;
Более всего строки Паскаля и строки с завершающим нулем от-
личаются интенсивностью использования указателей. Borland Pascal
выполняет операции с этими указателями, используя набор правил
расширенного синтаксиса. Кроме того, в Borland Pascal имеется
встроенный тип PChar, который представляет собой указатель на
строку с завершающим нулем. В модуле System тип PChar определяет-
ся следующим образом:
type PChar = ^Char;
Правилами расширенного синтаксиса управляет директива компи-
лятора $X. В состоянии {$X+} (по умолчанию) расширенный синтаксис
разрешен. Правила расширенного синтаксиса описываются в следующих
разделах.
Символьные указатели и строковые литералы
При разрешении расширенного синтаксиса строковый литерал
совместим по присваиванию с типом PChar. Это означает, что пере-
менной типа PChar можно присвоить строковый литерал. Например:
var
P: PChar;
.
.
begin
P := 'Привет...';
end;
В результате такого присваивания указатель указывает на об-
ласть памяти, содержащую строку с завершающим нулем, являющуюся
копией строкового литерала. Компилятор записывает строковые лите-
ралы в сегмент данных, аналогично описанию "скрытых" типизирован-
ных констант:
const
TempString: array[0..14] of Char = 'Привет...'#0;
var
P: PChar;
.
.
begin
P := @TempString;
end;
Когда соответствующие формальные параметры имеют тип Char,
строковые литералы вы можете использовать как фактические пара-
метры при вызовах процедур и функций. Например, если имеется про-
цедура с описанием:
procedure PrintStr(Str: PChar);
то допустимы следующие вызовы процедуры:
procedure PrintStr('Строка для проверки');
PrintStr(#10#13);
Аналогично тому, как это происходит при присваивании, компи-
лятор генерирует строку с завершающим нулем, представляющую собой
копию литеральной строки в сегменте данных, и передает указатель
на эту область памяти в параметре Str процедуры PrintStr.
Наконец, типизированная константа типа PChar может инициали-
зироваться строковой константой. Это справедливо также для струк-
турных типов, таких как массивы PChar и записи, а также объекты
PChar.
const
Message: PChar = 'Program terminated';
Prompt: PChar = 'Enter values: ';
Digits; array [0..9] of PChar = {
'Zero', 'One', 'Two', 'Three', 'Four', 'Five',
'Six', 'Seven', Eight', 'Nine'};
Строковая выражение-константа всегда вычисляется как строка
Паскаля, даже если она инициализируется как типизированная конс-
танта типа PChar. Таким образом, строковое выражение-константа
всегда ограничено длиной в 255 символов.
Символьные указатели и символьные массивы
Если вы с помощью директивы $X разрешаете расширенный син-
таксис, то символьный массив с нулевой базой совместим с типом
PChar. Это означает, что там, где предполагается использование
типа PChar, может использоваться символьный массив с нулевой ба-
зой. Когда символьный массив используется вместо значения PChar,
компилятор преобразует символьный массив в указатель-константу,
значение которой соответствует адресу первого элемента массива.
Например:
var
A: array[0..63] of Char;
P: PChar;
.
.
.
begin
P := A;
PrintStr(A);
PrintStr(P);
end;
Благодаря оператору присваивания P теперь указывает на пер-
вый элемент массива A, поэтому PrintStr вызывается дважды с одним
и тем же значением.
Вы можете инициализировать типизованную константу, имеющую
тип символьного массива с нулевой базой, с помощью строкового ли-
терала, имеющего меньшую длину, чем размер массива. Оставшиеся
символы устанавливаются в значение NULL (#0), и массив будет со-
держать строку с завершающим нулем.
type
TFileName = array[0..79] of Char;
const
FileNameBuf: TfileName = 'TEST.PAS';
FileNamePtr: PCahr = FileNameBuf;
Индексирование символьного указателя
Так как символьный массив с нулевой базой совместим с сим-
вольным указателем, символьный указатель можно индексировать ана-
логично символьному массиву с нулевой базой.
var
A: array[0..63] of Char;
P: PChar;
Ch: Char;
.
.
.
begin
P := A;
Ch := A[5];
Ch := P[5];
end;
Оба последних присваивания присваивают Ch значение, содержа-
щееся в шестом символе-элементе A.
При индексировании символьного указателя индекс задает безз-
наковое смещение, которое добавляется к указателю перед его разы-
менованием. Таким образом, P[0] эквивалентно P^ и задает символ,
на который указывает P. P[1] задает символ справа от того, на ко-
торый указывает P, P[2] задает следующий символ и т.д. Для целей
индексирования PChar ведет себя таким образом, как если бы он
описывался:
type
TCharArray = array[0..65535] of Char;
Pchar = ^TCharArray;
Компилятор при индексировании символьного указателя не вы-
полняет проверку диапазона, так как у него нет информации о типе,
по которой можно определить максимальную длину строки с завершаю-
щим нулем, на которую указывает символьный указатель.
Показанная ниже функция StrUpper иллюстрирует использование
символьного указателя для преобразования строки с завершающим ну-
лем в верхний регистр.
function StrUpper(Srt: Pchar): Pchar;
var
I: Word;
begin
I := 0;
while Str[I] <> #0 do
begin
Str[I] := UpCase(Str[I]);
Inc(I);
end;
StrUpper := Str;
end;
Обратите внимание, что StrUppper - это функция, а не проце-
дура, и что она всегда возвращает значение, которое передавалось
ей в качестве параметра. Так как расширенный синтаксис допускает
игнорирование результата функции, StrUpper может интерпретиро-
ваться, как процедура:
StrUpper(A);
PrintStr(A);
Однако, StrUpper всегда возвращает передаваемое ей значение,
приведенные выше операторы можно скомбинировать в один:
PrintStr(StrUpper(A));
Вложенные вызовы функций работы со строками с завершающим
нулем могут оказаться очень удобными, когда вы хотите указать оп-
ределенную взаимосвязь между последовательными операциями со
строками.
Операции с символьными указателями
Расширенный синтаксис Borland Pascal позволяет использовать
для работы с символьными указателями отдельные операции. Для уве-
личения или уменьшения смещения в значении указателя можно ис-
пользовать операции плюс (+) и минус (-). Операцию минус (-) мож-
но использовать для вычисления расстояния (разности смещений)
между двумя символьными указателями. Предположим, что P и Q
представляют собой значения тип PChar, а I - значение типа Word.
Тогда допустимы следующие конструкции:
P + I I прибавляется к смещению P
I + P I прибавляется к смещению P
P - I I вычитается из смещения P
P - Q Смещение Q вычитается из смещения P
В операциях P + I и I + P I прибавляется к адресу, задавае-
мому P. При этом получается указатель, который указывает на I
символов после P. В операции P - I I вычитается из адреса, зада-
ваемого P, и получается указатель, указывающий на I символов до
P.
Операция P - Q вычисляет расстояние между Q (младший адрес)
и P (старший адрес). При этом возвращается результат типа Word,
показывающий число символов между Q и P. Эта операция предполага-
ет, что P и Q указывают на один и тот же массив символов. Если
эти два указателя указывают на разные символьные массивы, то ре-
зультат непредсказуем.
Стандартный синтаксис Borland Pascal позволяет при сравнении
указателей определять только их равенство или неравенство. Расши-
ренный синтаксис (разрешенный по директиве компилятора {$X+})
позволяет применять операции <, >, <= и <= к значениям PChar. За-
метим, однако, что при таких проверках предполагается, что два
сравниваемых указателя указывают на один и тот же массив симво-
лов. По этой причине сравниваются только смещения указателей. Ес-
ли два указателя указывают на различные символьные массивы, то
результат не определен.
var
A, B: array[0..79] of Char;
P, Q: PChar;
begin
P := A; { P указывает на A[0] }
Q := A + 5; { Q указывает на A[5] }
if P < Q then ...; { допустимая проверка,
результат - True }
Q := B; { Q указывает на B[0] }
if P < Q then ...; { результат не определен }
end;
Подробнее об операциях с PChar рассказывается в Главе 6.
Строки с завершающим нулем и стандартные процедуры
Расширенный синтаксис Borland Pascal позволяет применять к
символьным массивам с нулевой базой стандартные процедуры Read,
ReadLn и Val, а к символьным массива с нулевой базой и символьным
указателям - стандартные процедуры Write, WriteLn, Val, Assign и
Rename. Более подробные описания этих процедур можно найти в Гла-
ве 1 ("Справочник по библиотеке") "Справочного руководства прог-
раммиста".
Пример использования функций с завершающим нулем
Приведем пример исходного кода, показывающий, как можно ис-
пользовать некоторые функции обработки строк. Этот пример исполь-
зован при разработке функции FileSplit в модуле WinDos.
{ максимальные размеры компонентов имени файла }
const
fsPathName = 79; { имя маршрута }
fsDirectory = 67; { имя каталога }
fsFileName = 8; { имя файла }
fsExtension = 4; { расширение имени файла }
{ флаги, возвращаемые FileSplit }
const
fcWildcards = $0008 { трафаретные символы }
fcDirectory = $0004 { имя каталога }
fcFileName = $0002 { имя файла }
fcExtension = $0001 { расширение имени файла }
{ FileSplit разбивает имя файла, заданное маршрутом, на три }
{ компонента. Dir принимает значение диска и каталога с }
{ предшествующей и завершающей обратной косой чертой, Name }
{ принимает значение имени файла, а Ext - расширения с }
{ предшествующей точкой. Если компонент строки-параметра }
{ равен NIL, то соответствующая часть маршрута не }
{ записывается. Если маршрут не содержит данного компонента, }
{ то возвращаемая строка компонента будет пустой. }
{ Максимальные длины строк, возвращаемых в Dir, Name и Ext, }
{ определяются битовыми масками fsDirectory, fsFileName, }
{ fsExtension. Возвращаемое значение представляет собой }
{ комбинацию битовых масок fсDirectory, fсFileName и }
{ fсExtension, показывающую, какие компоненты присутствуют в }
{ маршруте. Если имя и расширение содержат трафаретные }
{ символы (* и ?), то в возвращаемом значении устанавливается }
{ флаг fcWildcards. }
function FileSplit(Path, Dir, Name, Ext: PChar): Word;
var
DirLen, NameLEn, Flags: Word;
NamePtr, ExtPtr: PChar;
begin
NamePtr := StrRScan(Path, '/');
if NamePtr = nil then NamePtr := StrRScan(Path, ':');
if NamePtr = nil then NamePtr := Path else Inc(NamePtr);
ExtPtr := StrScan(NamePtr, '.');
if ExtPtr = nil then ExtPtr := StrEnd(NamePtr);
DirLen := NamePtr - Path;
if DirLen > fsDirectory then DirLen := fsDirectory;
NameLen := ExtPtr - NamePtr;
if NameLen > fsFilename then NameLen := fsFileName;
Flags := 0;
if (StrScan(NamePtr, '?') <> nil) or
(StrScan(NamePtr, '*') <> nil) then
Falgs := fcWildcards;
if DirLen <> 0 then Flags := Flags or fcDirectory;
if NameLen <> 0 then Flags := Flags or fcFilename;
if ExtPtr[0] <> #0 then Flags := Flags or fcExtension;
if Dir <> nil then StrLCopy(Dir, Path, DirLen);
if Name <> nil then StrLCopy(Name, NamePtr, NameLen);
if Ext <> nil then StrLCopy(Ext, ExtPtr, fsExtension);
FileSplit := Flags:
end;
Назад | Содержание | Вперед