В Borland Pascal поддерживается класс символьных строк,  которые называются строками, завершающимися нулем. Благодаря расширенному синтаксису Borland Pascal и модулю Strings ваши программы  (как для DOS,  так и для Windows) могут использовать строки с завершающим нулем путем задания в операторе uses модуля Strings.

В Borland  Pascal строки обычного типа (String) хранятся как  байт длины,  за которым следует последовательность символов. Максимальная длина строки в Паскале равна 255 символам.  Таким образом, строка Паскаля занимает от 1 до 256 байт памяти.

Строки с завершающим нулем не содержат байта  длины.  Вместо  этого они  состоят из последовательности ненулевых символов, за  которыми следует символ NULL (#0).  Никаких ограничений на  длину  строк с завершающим нулем не накладывается, но 16-разрядная архитектура DOS и Windows ограничивает их размер 65535 символами.

Borland Pascal  не имеет встроенных подпрограмм,  предназначенных специально для работы со строками с завершающим нулем. Эти  функции вы  можете  найти в модуле Strings.  Среди них вы найдете  функцию StrPCopy,  которую можно  использовать  для копирования  строки Паскаля в строку с завершающим нулем, и StrPos, используемую для преобразования строки с завершающим нулем в строку Паскаля. Приведем краткое описание каждой функции:

  і  Функция  і Описание   і

 ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДґ

  і  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   і Преобразует  строку в верхний регистр и возвра-і

  і  і щает указатель на нее. і

  АДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДЩ

Строки с завершающим нулем хранятся в виде символьных массивов с нулевой базой (начинающихся с 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;